Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PCBB] Add testcases to verify separated DSCP_TO_TC_MAP on leaf router #6393

Merged
merged 7 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions tests/common/fixtures/duthost_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import collections
import ipaddress
import time
import json
from tests.common.helpers.assertions import pytest_assert
from tests.common.utilities import wait_until
from jinja2 import Template
Expand Down Expand Up @@ -419,3 +420,100 @@ def utils_create_test_vlans(duthost, cfg_facts, vlan_ports_list, vlan_intfs_dict
))
logger.info("Commands: {}".format(cmds))
duthost.shell_cmds(cmds=cmds)


@pytest.fixture(scope='module')
def dut_qos_maps(rand_selected_dut):
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can make it more generic by adding an argument for the expected map name?
Eg., in dual ToR t0 scenario, maybe AZURE_DOWNLINK will be introduced for downlink ports (sonic-net/sonic-buildimage#12605).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Now the code counts the dscp_to_tc_map for each port.

A module level fixture to get QoS map from DUT host.
Return a dict
{
"dscp_to_tc_map": {
"0":"1",
...
},
"tc_to_queue_map": {
"0":"0"
},
...
}
or an empty dict if failed to parse the output
"""
maps = {}
try:
# port_qos_map
maps['port_qos_map'] = json.loads(rand_selected_dut.shell("sonic-cfggen -d --var-json 'PORT_QOS_MAP'")['stdout'])
# dscp_to_tc_map
maps['dscp_to_tc_map'] = json.loads(rand_selected_dut.shell("sonic-cfggen -d --var-json 'DSCP_TO_TC_MAP'")['stdout'])
# tc_to_queue_map
maps['tc_to_queue_map'] = json.loads(rand_selected_dut.shell("sonic-cfggen -d --var-json 'TC_TO_QUEUE_MAP'")['stdout'])
# tc_to_priority_group_map
maps['tc_to_priority_group_map'] = json.loads(rand_selected_dut.shell("sonic-cfggen -d --var-json 'TC_TO_PRIORITY_GROUP_MAP'")['stdout'])
# tc_to_dscp_map
maps['tc_to_dscp_map'] = json.loads(rand_selected_dut.shell("sonic-cfggen -d --var-json 'TC_TO_DSCP_MAP'")['stdout'])
except:
pass
return maps


def separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps):
"""
A helper function to check if separated DSCP_TO_TC_MAP is applied to
downlink/unlink ports.
"""
dscp_to_tc_map_names = set()
for port_name, qos_map in dut_qos_maps['port_qos_map'].iteritems():
if port_name == "global":
continue
dscp_to_tc_map_names.add(qos_map.get("dscp_to_tc_map", ""))
if len(dscp_to_tc_map_names) > 1:
return True
return False


def load_dscp_to_pg_map(duthost, port, dut_qos_maps):
"""
Helper function to calculate DSCP to PG map for a port.
The map is derived from DSCP_TO_TC_MAP + TC_TO_PG_MAP
return a dict like {0:0, 1:1...}
"""
try:
port_qos_map = dut_qos_maps['port_qos_map']
dscp_to_tc_map_name = port_qos_map[port]['dscp_to_tc_map'].split('|')[-1].strip(']')
tc_to_pg_map_name = port_qos_map[port]['tc_to_pg_map'].split('|')[-1].strip(']')
# Load dscp_to_tc_map
dscp_to_tc_map = dut_qos_maps['dscp_to_tc_map'][dscp_to_tc_map_name]
# Load tc_to_pg_map
tc_to_pg_map = dut_qos_maps['tc_to_priority_group_map'][tc_to_pg_map_name]
# Calculate dscp to pg map
dscp_to_pg_map = {}
for dscp, tc in dscp_to_tc_map.items():
dscp_to_pg_map[dscp] = tc_to_pg_map[tc]
return dscp_to_pg_map
except:
logger.error("Failed to retrieve dscp to pg map for port {} on {}".format(port, duthost.hostname))
return {}


def load_dscp_to_queue_map(duthost, port, dut_qos_maps):
"""
Helper function to calculate DSCP to Queue map for a port.
The map is derived from DSCP_TO_TC_MAP + TC_TO_QUEUE_MAP
return a dict like {0:0, 1:1...}
"""
try:
port_qos_map = dut_qos_maps['port_qos_map']
dscp_to_tc_map_name = port_qos_map[port]['dscp_to_tc_map'].split('|')[-1].strip(']')
tc_to_queue_map_name = port_qos_map[port]['tc_to_queue_map'].split('|')[-1].strip(']')
# Load dscp_to_tc_map
dscp_to_tc_map = dut_qos_maps['dscp_to_tc_map'][dscp_to_tc_map_name][dscp_to_tc_map_name]
# Load tc_to_queue_map
tc_to_queue_map = dut_qos_maps['tc_to_queue_map'][tc_to_queue_map_name]
# Calculate dscp to queue map
dscp_to_queue_map = {}
for dscp, tc in dscp_to_tc_map.items():
dscp_to_queue_map[dscp] = tc_to_queue_map[tc]
return dscp_to_queue_map
except:
logger.error("Failed to retrieve dscp to queue map for port {} on {}".format(port, duthost.hostname))
return {}
37 changes: 33 additions & 4 deletions tests/qos/qos_sai_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from tests.common.dualtor.mux_simulator_control import toggle_all_simulator_ports, get_mux_status, check_mux_status, validate_check_result
from tests.common.dualtor.constants import UPPER_TOR, LOWER_TOR
from tests.common.utilities import check_qos_db_fv_reference_with_table
from tests.common.fixtures.duthost_utils import dut_qos_maps, separated_dscp_to_tc_map_on_uplink
from tests.common.utilities import wait_until

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -469,7 +470,7 @@ def __buildTestPorts(self, request, testPortIds, testPortIps, src_port_ids, dst_
@pytest.fixture(scope='class', autouse=True)
def dutConfig(
self, request, duthosts, enum_rand_one_per_hwsku_frontend_hostname,
enum_frontend_asic_index, lower_tor_host, tbinfo, dualtor_ports
enum_frontend_asic_index, lower_tor_host, tbinfo, dualtor_ports, dut_qos_maps
):
"""
Build DUT host config pertaining to QoS SAI tests
Expand All @@ -491,6 +492,12 @@ def dutConfig(
dutLagInterfaces = []
dutPortIps = {}
testPortIps = {}
uplinkPortIds = []
uplinkPortIps = []
uplinkPortNames = []
downlinkPortIds = []
downlinkPortIps = []
downlinkPortNames = []

mgFacts = duthost.get_extended_minigraph_facts(tbinfo)
topo = tbinfo["topo"]["name"]
Expand All @@ -499,7 +506,7 @@ def dutConfig(

testPortIds = []
# LAG ports in T1 TOPO need to be removed in Mellanox devices
if topo in self.SUPPORTED_T0_TOPOS or topo in self.SUPPORTED_T1_TOPOS or isMellanoxDevice(duthost):
if topo in self.SUPPORTED_T0_TOPOS or isMellanoxDevice(duthost):
pytest_assert(
not duthost.sonichost.is_multi_asic, "Fixture not supported on T0 multi ASIC"
)
Expand Down Expand Up @@ -538,12 +545,13 @@ def dutConfig(
testPortIps = self.__assignTestPortIps(mgFacts)

elif topo in self.SUPPORTED_T1_TOPOS:
use_separated_upkink_dscp_tc_map = separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps)
for iface,addr in dut_asic.get_active_ip_interfaces(tbinfo).items():
vlan_id = None
if iface.startswith("Ethernet"):
if "." in iface:
iface, vlan_id = iface.split(".")
portIndex = mgFacts["minigraph_ptf_indices"][iface]
portName, vlan_id = iface.split(".")
portIndex = mgFacts["minigraph_ptf_indices"][portName]
portIpMap = {'peer_addr': addr["peer_ipv4"]}
if vlan_id is not None:
portIpMap['vlan_id'] = vlan_id
Expand All @@ -555,6 +563,18 @@ def dutConfig(
portIndex = mgFacts["minigraph_ptf_indices"][portName]
portIpMap = {'peer_addr': addr["peer_ipv4"]}
dutPortIps.update({portIndex: portIpMap})
# If the leaf router is using separated DSCP_TO_TC_MAP on uplink/downlink ports.
# we also need to test them separately
if use_separated_upkink_dscp_tc_map:
neighName = mgFacts["minigraph_neighbors"].get(portName, {}).get("name", "").lower()
lolyu marked this conversation as resolved.
Show resolved Hide resolved
if 't0' in neighName:
downlinkPortIds.append(portIndex)
downlinkPortIps.append(addr["peer_ipv4"])
downlinkPortNames.append(portName)
elif 't2' in neighName:
uplinkPortIds.append(portIndex)
uplinkPortIps.append(addr["peer_ipv4"])
uplinkPortNames.append(portName)

testPortIds = sorted(dutPortIps.keys())

Expand Down Expand Up @@ -624,6 +644,15 @@ def dutConfig(
testPortIds = dualTorPortIndexes

testPorts = self.__buildTestPorts(request, testPortIds, testPortIps, src_port_ids, dst_port_ids)
# Update the uplink/downlink ports to testPorts
testPorts.update({
"uplink_port_ids": uplinkPortIds,
"uplink_port_ips": uplinkPortIps,
"uplink_port_names": uplinkPortNames,
"downlink_port_ids": downlinkPortIds,
"downlink_port_ips": downlinkPortIps,
"downlink_port_names": downlinkPortNames
})
dutinterfaces = {}
for port, index in mgFacts["minigraph_ptf_indices"].items():
if 'Ethernet-Rec' not in port and 'Ethernet-IB' not in port:
Expand Down
124 changes: 119 additions & 5 deletions tests/qos/test_qos_sai.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
import time
import json

from tests.common.fixtures.conn_graph_facts import fanout_graph_facts, conn_graph_facts # lgtm[py/unused-import]
from tests.common.fixtures.conn_graph_facts import fanout_graph_facts, conn_graph_facts
from tests.common.fixtures.duthost_utils import dut_qos_maps, separated_dscp_to_tc_map_on_uplink, load_dscp_to_pg_map # lgtm[py/unused-import]
from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory # lgtm[py/unused-import]
from tests.common.fixtures.ptfhost_utils import copy_saitests_directory # lgtm[py/unused-import]
from tests.common.fixtures.ptfhost_utils import change_mac_addresses # lgtm[py/unused-import]
Expand Down Expand Up @@ -862,23 +863,28 @@ def testQosSaiLossyQueueVoq(
setup_markings_dut(duthost, localhost, **original_voq_markings)

def testQosSaiDscpQueueMapping(
self, ptfhost, dutTestParams, dutConfig
self, duthost, ptfhost, dutTestParams, dutConfig, dut_qos_maps
):
"""
Test QoS SAI DSCP to queue mapping

Args:
duthost (AnsibleHost): The DUT host
ptfhost (AnsibleHost): Packet Test Framework (PTF)
dutTestParams (Fixture, dict): DUT host test params
dutConfig (Fixture, dict): Map of DUT config containing dut interfaces, test port IDs, test port IPs,
and test ports

dut_qos_maps(Fixture): A fixture, return qos maps on DUT host
Returns:
None

Raises:
RunAnsibleModuleFail if ptf test fails
"""
# Skip the regular dscp to pg mapping test. Will run another test case instead.
if separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps):
pytest.skip("Skip this test since separated DSCP_TO_TC_MAP is applied")

testParams = dict()
testParams.update(dutTestParams["basicParams"])
testParams.update({
Expand All @@ -896,6 +902,59 @@ def testQosSaiDscpQueueMapping(
testParams=testParams
)

@pytest.mark.parametrize("direction", ["downstream", "upstream"])
def testQosSaiSeparatedDscpQueueMapping(self, duthost, ptfhost, dutTestParams, dutConfig, direction, dut_qos_maps):
"""
Test QoS SAI DSCP to queue mapping.
We will have separated DSCP_TO_TC_MAP for uplink/downlink ports on T1 if PCBB enabled.
This test case will generate both upstream and downstream traffic to verify the behavior

Args:
duthost (AnsibleHost): The DUT host
ptfhost (AnsibleHost): Packet Test Framework (PTF)
dutTestParams (Fixture, dict): DUT host test params
dutConfig (Fixture, dict): Map of DUT config containing dut interfaces, test port IDs, test port IPs,
and test ports
direction (str): upstream/downstream
dut_qos_maps(Fixture): A fixture, return qos maps on DUT host
Returns:
None

Raises:
RunAnsibleModuleFail if ptf test fails
"""
# Only run this test when separated DSCP_TO_TC_MAP is defined
if not separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps):
pytest.skip("Skip this test since separated DSCP_TO_TC_MAP is not applied")

testParams = dict()
testParams.update(dutTestParams["basicParams"])
testParams.update({
"hwsku": dutTestParams['hwsku'],
"dual_tor_scenario": True
})
if direction == "downstream":
testParams.update({
"dst_port_id": dutConfig["testPorts"]["downlink_port_ids"][0],
"dst_port_ip": dutConfig["testPorts"]["downlink_port_ips"][0],
"src_port_id": dutConfig["testPorts"]["uplink_port_ids"][0],
"src_port_ip": dutConfig["testPorts"]["uplink_port_ips"][0]
})
testParams.update({"leaf_downstream": True})
else:
testParams.update({
"dst_port_id": dutConfig["testPorts"]["uplink_port_ids"][0],
"dst_port_ip": dutConfig["testPorts"]["uplink_port_ips"][0],
"src_port_id": dutConfig["testPorts"]["downlink_port_ids"][0],
"src_port_ip": dutConfig["testPorts"]["downlink_port_ips"][0]
})
testParams.update({"leaf_downstream": False})

self.runPtfTest(
ptfhost, testCase="sai_qos_tests.DscpMappingPB",
testParams=testParams
)

def testQosSaiDot1pQueueMapping(
self, ptfhost, dutTestParams, dutConfig
):
Expand Down Expand Up @@ -1258,17 +1317,18 @@ def testQosSaiQSharedWatermark(
)

def testQosSaiDscpToPgMapping(
self, request, ptfhost, dutTestParams, dutConfig,
self, duthost, request, ptfhost, dutTestParams, dutConfig, dut_qos_maps
):
"""
Test QoS SAI DSCP to PG mapping ptf test

Args:
duthost (AnsibleHost): The DUT host
ptfhost (AnsibleHost): Packet Test Framework (PTF)
dutTestParams (Fixture, dict): DUT host test params
dutConfig (Fixture, dict): Map of DUT config containing dut interfaces, test port IDs, test port IPs,
and test ports

dut_qos_maps(Fixture): A fixture, return qos maps on DUT host
Returns:
None

Expand All @@ -1280,6 +1340,9 @@ def testQosSaiDscpToPgMapping(
disableTest = False
if disableTest:
pytest.skip("DSCP to PG mapping test disabled")
# Skip the regular dscp to pg mapping test. Will run another test case instead.
if separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps):
pytest.skip("Skip this test since separated DSCP_TO_TC_MAP is applied")

testParams = dict()
testParams.update(dutTestParams["basicParams"])
Expand All @@ -1294,6 +1357,57 @@ def testQosSaiDscpToPgMapping(
testParams=testParams
)

@pytest.mark.parametrize("direction", ["downstream", "upstream"])
def testQosSaiSeparatedDscpToPgMapping(self, duthost, request, ptfhost, dutTestParams, dutConfig, direction, dut_qos_maps):
"""
Test QoS SAI DSCP to PG mapping ptf test.
Since we are using different DSCP_TO_TC_MAP on uplink/downlink port, the test case also need to
run separately

Args:
duthost (AnsibleHost)
ptfhost (AnsibleHost): Packet Test Framework (PTF)
dutTestParams (Fixture, dict): DUT host test params
dutConfig (Fixture, dict): Map of DUT config containing dut interfaces, test port IDs, test port IPs,
and test ports
direction (str): downstream or upstream
dut_qos_maps(Fixture): A fixture, return qos maps on DUT host
Returns:
None

Raises:
RunAnsibleModuleFail if ptf test fails
"""
if not separated_dscp_to_tc_map_on_uplink(duthost, dut_qos_maps):
pytest.skip("Skip this test since separated DSCP_TO_TC_MAP is not applied")

testParams = dict()
testParams.update(dutTestParams["basicParams"])
if direction == "downstream":
testParams.update({
"dst_port_id": dutConfig["testPorts"]["downlink_port_ids"][0],
"dst_port_ip": dutConfig["testPorts"]["downlink_port_ips"][0],
"src_port_id": dutConfig["testPorts"]["uplink_port_ids"][0],
"src_port_ip": dutConfig["testPorts"]["uplink_port_ips"][0]
})
src_port_name = dutConfig["testPorts"]["uplink_port_names"][0]
else:
testParams.update({
"dst_port_id": dutConfig["testPorts"]["uplink_port_ids"][0],
"dst_port_ip": dutConfig["testPorts"]["uplink_port_ips"][0],
"src_port_id": dutConfig["testPorts"]["downlink_port_ids"][0],
"src_port_ip": dutConfig["testPorts"]["downlink_port_ips"][0]
})
src_port_name = dutConfig["testPorts"]["downlink_port_names"][0]

testParams['dscp_to_pg_map'] = load_dscp_to_pg_map(duthost, src_port_name, dut_qos_maps)

self.runPtfTest(
ptfhost, testCase="sai_qos_tests.DscpToPgMapping",
testParams=testParams
)


def testQosSaiDwrrWeightChange(
self, ptfhost, dutTestParams, dutConfig, dutQosConfig,
updateSchedProfile
Expand Down
Loading