diff --git a/doc/Configuration.md b/doc/Configuration.md index a619531f70f3..0c91e16db5d8 100644 --- a/doc/Configuration.md +++ b/doc/Configuration.md @@ -742,6 +742,26 @@ instance is supported in SONiC. ``` + +### MPLS_TC_TO_TC_MAP +``` +{ +"MPLS_TC_TO_TC_MAP": { + "AZURE": { + "0": "0", + "1": "1", + "2": "1", + "3": "2", + "4": "2", + "5": "3", + "6": "3", + "7": "4" + } + } +} + +``` + ### FLEX_COUNTER_TABLE ``` diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 4da3cb3b2a86..47fc9cd32c0e 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -31,6 +31,7 @@ Stores information for physical switch ports managed by the switch chip. Ports t ;QOS Mappings map_dscp_to_tc = ref_hash_key_reference map_tc_to_queue = ref_hash_key_reference + map_mpls_tc_to_tc = ref_hash_key_reference Example: 127.0.0.1:6379> hgetall PORT_TABLE:ETHERNET4 @@ -38,6 +39,8 @@ Stores information for physical switch ports managed by the switch chip. Ports t 2) "AZURE" 3) "tc_to_queue_map" 4) "AZURE" + 5) "mpls_tc_to_tc_map" + 6) "AZURE" --------------------------------------------- ### INTF_TABLE @@ -279,6 +282,28 @@ and reflects the LAG ports into the redis under: `LAG_TABLE::port` 9) "9" 10) "8" +--------------------------------------------- +### MPLS\_TC\_TO\_TC\_MAP\_TABLE + ; MPLS TC to TC map + ;SAI mapping - qos_map object with SAI_QOS_MAP_ATTR_TYPE == sai_qos_map_type_t::SAI_QOS_MAP_EXP_TO_TC + key = "MPLS_TC_TO_TC_MAP_TABLE:"name + ;field value + mpls_tc_value = 1*DIGIT + tc_value = 1*DIGIT + + Example: + 127.0.0.1:6379> hgetall "MPLS_TC_TO_TC_MAP_TABLE:AZURE" + 1) "0" ;mpls_tc + 2) "3" ;tc + 3) "1" + 4) "5" + 5) "2" + 6) "5" + 7) "3" + 8) "7" + 9) "4" + 10) "8" + --------------------------------------------- ### SCHEDULER_TABLE ; Scheduler table diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index b06521b02590..eebbaa720b88 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -186,6 +186,7 @@ bool OrchDaemon::init() CFG_TC_TO_QUEUE_MAP_TABLE_NAME, CFG_SCHEDULER_TABLE_NAME, CFG_DSCP_TO_TC_MAP_TABLE_NAME, + CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, CFG_DOT1P_TO_TC_MAP_TABLE_NAME, CFG_QUEUE_TABLE_NAME, CFG_PORT_QOS_MAP_TABLE_NAME, diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 771ffbcd8a5f..3c422c67d669 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -45,6 +45,7 @@ enum { // field_name is what is expected in CONFIG_DB PORT_QOS_MAP table map qos_to_attr_map = { {dscp_to_tc_field_name, SAI_PORT_ATTR_QOS_DSCP_TO_TC_MAP}, + {mpls_tc_to_tc_field_name, SAI_PORT_ATTR_QOS_MPLS_EXP_TO_TC_MAP}, {dot1p_to_tc_field_name, SAI_PORT_ATTR_QOS_DOT1P_TO_TC_MAP}, {tc_to_queue_field_name, SAI_PORT_ATTR_QOS_TC_TO_QUEUE_MAP}, {tc_to_pg_map_field_name, SAI_PORT_ATTR_QOS_TC_TO_PRIORITY_GROUP_MAP}, @@ -60,6 +61,7 @@ map scheduler_meter_map = { type_map QosOrch::m_qos_maps = { {CFG_DSCP_TO_TC_MAP_TABLE_NAME, new object_reference_map()}, + {CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, new object_reference_map()}, {CFG_DOT1P_TO_TC_MAP_TABLE_NAME, new object_reference_map()}, {CFG_TC_TO_QUEUE_MAP_TABLE_NAME, new object_reference_map()}, {CFG_SCHEDULER_TABLE_NAME, new object_reference_map()}, @@ -73,6 +75,7 @@ type_map QosOrch::m_qos_maps = { map qos_to_ref_table_map = { {dscp_to_tc_field_name, CFG_DSCP_TO_TC_MAP_TABLE_NAME}, + {mpls_tc_to_tc_field_name, CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME}, {dot1p_to_tc_field_name, CFG_DOT1P_TO_TC_MAP_TABLE_NAME}, {tc_to_queue_field_name, CFG_TC_TO_QUEUE_MAP_TABLE_NAME}, {tc_to_pg_map_field_name, CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME}, @@ -238,6 +241,61 @@ task_process_status QosOrch::handleDscpToTcTable(Consumer& consumer) return dscp_tc_handler.processWorkItem(consumer); } +bool MplsTcToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_attribute_t list_attr; + sai_qos_map_list_t exp_map_list; + exp_map_list.count = (uint32_t)kfvFieldsValues(tuple).size(); + exp_map_list.list = new sai_qos_map_t[exp_map_list.count](); + uint32_t ind = 0; + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++, ind++) + { + exp_map_list.list[ind].key.mpls_exp = (uint8_t)stoi(fvField(*i)); + exp_map_list.list[ind].value.tc = (uint8_t)stoi(fvValue(*i)); + SWSS_LOG_DEBUG("key.exp:%d, value.tc:%d", exp_map_list.list[ind].key.mpls_exp, exp_map_list.list[ind].value.tc); + } + list_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + list_attr.value.qosmap.count = exp_map_list.count; + list_attr.value.qosmap.list = exp_map_list.list; + attributes.push_back(list_attr); + return true; +} + +sai_object_id_t MplsTcToTcMapHandler::addQosItem(const vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object; + vector qos_map_attrs; + + sai_attribute_t qos_map_attr; + qos_map_attr.id = SAI_QOS_MAP_ATTR_TYPE; + qos_map_attr.value.u32 = SAI_QOS_MAP_TYPE_MPLS_EXP_TO_TC; + qos_map_attrs.push_back(qos_map_attr); + + qos_map_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + qos_map_attr.value.qosmap.count = attributes[0].value.qosmap.count; + qos_map_attr.value.qosmap.list = attributes[0].value.qosmap.list; + qos_map_attrs.push_back(qos_map_attr); + + sai_status = sai_qos_map_api->create_qos_map(&sai_object, gSwitchId, (uint32_t)qos_map_attrs.size(), qos_map_attrs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create exp_to_tc map. status:%d", sai_status); + return SAI_NULL_OBJECT_ID; + } + SWSS_LOG_DEBUG("created QosMap object:%" PRIx64, sai_object); + return sai_object; +} + +task_process_status QosOrch::handleMplsTcToTcTable(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + MplsTcToTcMapHandler mpls_tc_to_tc_handler; + return mpls_tc_to_tc_handler.processWorkItem(consumer); +} + bool Dot1pToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) { SWSS_LOG_ENTER(); @@ -760,6 +818,7 @@ void QosOrch::initTableHandlers() { SWSS_LOG_ENTER(); m_qos_handler_map.insert(qos_handler_pair(CFG_DSCP_TO_TC_MAP_TABLE_NAME, &QosOrch::handleDscpToTcTable)); + m_qos_handler_map.insert(qos_handler_pair(CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, &QosOrch::handleMplsTcToTcTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_DOT1P_TO_TC_MAP_TABLE_NAME, &QosOrch::handleDot1pToTcTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_TC_TO_QUEUE_MAP_TABLE_NAME, &QosOrch::handleTcToQueueTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_SCHEDULER_TABLE_NAME, &QosOrch::handleSchedulerTable)); diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index d15f4e3a735e..e65c3fa028a2 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -8,6 +8,7 @@ #include "portsorch.h" const string dscp_to_tc_field_name = "dscp_to_tc_map"; +const string mpls_tc_to_tc_field_name = "mpls_tc_to_tc_map"; const string dot1p_to_tc_field_name = "dot1p_to_tc_map"; const string pfc_to_pg_map_name = "pfc_to_pg_map"; const string pfc_to_queue_map_name = "pfc_to_queue_map"; @@ -70,6 +71,13 @@ class DscpToTcMapHandler : public QosMapHandler sai_object_id_t addQosItem(const vector &attributes) override; }; +class MplsTcToTcMapHandler : public QosMapHandler +{ +public: + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) override; + sai_object_id_t addQosItem(const vector &attributes) override; +}; + class Dot1pToTcMapHandler : public QosMapHandler { public: @@ -137,6 +145,7 @@ class QosOrch : public Orch void initTableHandlers(); task_process_status handleDscpToTcTable(Consumer& consumer); + task_process_status handleMplsTcToTcTable(Consumer& consumer); task_process_status handleDot1pToTcTable(Consumer& consumer); task_process_status handlePfcPrioToPgTable(Consumer& consumer); task_process_status handlePfcToQueueTable(Consumer& consumer); diff --git a/tests/test_qos_map.py b/tests/test_qos_map.py index 1970d5f60b7b..51304d8b1fbf 100644 --- a/tests/test_qos_map.py +++ b/tests/test_qos_map.py @@ -1,6 +1,4 @@ -import pytest import json -import sys import time from swsscommon import swsscommon @@ -18,8 +16,22 @@ "7": "7", } +CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME = "MPLS_TC_TO_TC_MAP" +CFG_MPLS_TC_TO_TC_MAP_KEY = "AZURE_MPLS_TC" +MPLS_TC_TO_TC_MAP = { + "0": "0", + "1": "4", + "2": "1", + "3": "3", + "4": "5", + "5": "2", + "6": "7", + "7": "6", +} + CFG_PORT_QOS_MAP_TABLE_NAME = "PORT_QOS_MAP" -CFG_PORT_QOS_MAP_FIELD = "dot1p_to_tc_map" +CFG_PORT_QOS_DOT1P_MAP_FIELD = "dot1p_to_tc_map" +CFG_PORT_QOS_MPLS_TC_MAP_FIELD = "mpls_tc_to_tc_map" CFG_PORT_TABLE_NAME = "PORT" @@ -39,7 +51,6 @@ def create_dot1p_profile(self): def find_dot1p_profile(self): found = False dot1p_tc_map_raw = None - dot1p_tc_map_key = None tbl = swsscommon.Table(self.asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_QOS_MAP") keys = tbl.getKeys() for key in keys: @@ -50,7 +61,6 @@ def find_dot1p_profile(self): if fv[0] == "SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST": dot1p_tc_map_raw = fv[1] elif fv[0] == "SAI_QOS_MAP_ATTR_TYPE" and fv[1] == "SAI_QOS_MAP_TYPE_DOT1P_TO_TC": - dot1p_tc_map_key = key found = True if found: @@ -63,7 +73,7 @@ def find_dot1p_profile(self): def apply_dot1p_profile_on_all_ports(self): tbl = swsscommon.Table(self.config_db, CFG_PORT_QOS_MAP_TABLE_NAME) - fvs = swsscommon.FieldValuePairs([(CFG_PORT_QOS_MAP_FIELD, CFG_DOT1P_TO_TC_MAP_KEY)]) + fvs = swsscommon.FieldValuePairs([(CFG_PORT_QOS_DOT1P_MAP_FIELD, CFG_DOT1P_TO_TC_MAP_KEY)]) ports = swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME).getKeys() for port in ports: tbl.set(port, fvs) @@ -74,7 +84,7 @@ def apply_dot1p_profile_on_all_ports(self): def test_dot1p_cfg(self, dvs): self.connect_dbs(dvs) self.create_dot1p_profile() - oid, dot1p_tc_map_raw = self.find_dot1p_profile() + _, dot1p_tc_map_raw = self.find_dot1p_profile() dot1p_tc_map = json.loads(dot1p_tc_map_raw); for dot1p2tc in dot1p_tc_map['list']: @@ -86,7 +96,7 @@ def test_dot1p_cfg(self, dvs): def test_port_dot1p(self, dvs): self.connect_dbs(dvs) self.create_dot1p_profile() - oid, dot1p_tc_map_raw = self.find_dot1p_profile() + oid, _ = self.find_dot1p_profile() self.apply_dot1p_profile_on_all_ports() @@ -106,6 +116,87 @@ def test_port_dot1p(self, dvs): assert port_cnt == cnt +class TestMplsTc(object): + def connect_dbs(self, dvs): + self.asic_db = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.config_db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + + def create_mpls_tc_profile(self): + tbl = swsscommon.Table(self.config_db, CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME) + fvs = swsscommon.FieldValuePairs(list(MPLS_TC_TO_TC_MAP.items())) + tbl.set(CFG_MPLS_TC_TO_TC_MAP_KEY, fvs) + time.sleep(1) + + + def find_mpls_tc_profile(self): + found = False + mpls_tc_tc_map_raw = None + tbl = swsscommon.Table(self.asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_QOS_MAP") + keys = tbl.getKeys() + for key in keys: + (status, fvs) = tbl.get(key) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST": + mpls_tc_tc_map_raw = fv[1] + elif fv[0] == "SAI_QOS_MAP_ATTR_TYPE" and fv[1] == "SAI_QOS_MAP_TYPE_MPLS_EXP_TO_TC": + found = True + + if found: + break + + assert found == True + + return (key, mpls_tc_tc_map_raw) + + + def apply_mpls_tc_profile_on_all_ports(self): + tbl = swsscommon.Table(self.config_db, CFG_PORT_QOS_MAP_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([(CFG_PORT_QOS_MPLS_TC_MAP_FIELD, CFG_MPLS_TC_TO_TC_MAP_KEY)]) + ports = swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME).getKeys() + for port in ports: + tbl.set(port, fvs) + + time.sleep(1) + + + def test_mpls_tc_cfg(self, dvs): + self.connect_dbs(dvs) + self.create_mpls_tc_profile() + _, mpls_tc_tc_map_raw = self.find_mpls_tc_profile() + + mpls_tc_tc_map = json.loads(mpls_tc_tc_map_raw) + for mplstc2tc in mpls_tc_tc_map['list']: + mpls_tc = str(mplstc2tc['key']['mpls_exp']) + tc = str(mplstc2tc['value']['tc']) + assert tc == MPLS_TC_TO_TC_MAP[mpls_tc] + + + def test_port_mpls_tc(self, dvs): + self.connect_dbs(dvs) + self.create_mpls_tc_profile() + oid, _ = self.find_mpls_tc_profile() + + self.apply_mpls_tc_profile_on_all_ports() + + cnt = 0 + tbl = swsscommon.Table(self.asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + keys = tbl.getKeys() + for key in keys: + (status, fvs) = tbl.get(key) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_QOS_MPLS_EXP_TO_TC_MAP": + cnt += 1 + assert fv[1] == oid + + port_cnt = len(swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME).getKeys()) + assert port_cnt == cnt + + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying def test_nonflaky_dummy():