From 7b76d2e201f9f7d26cec7460f1335f7762b4ad01 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Wed, 11 Nov 2020 11:47:11 -0800 Subject: [PATCH] Copp Manager Changes (#1333) Introduce Copp Manager Subscribe to feature table and install trap based on feature status Support delete of Copp traps Co-authored-by: dgsudharsan --- cfgmgr/Makefile.am | 8 +- cfgmgr/coppmgr.cpp | 761 ++++++++++++++++++++++++++++++++++++++ cfgmgr/coppmgr.h | 103 ++++++ cfgmgr/coppmgrd.cpp | 93 +++++ orchagent/copporch.cpp | 775 +++++++++++++++++++++++---------------- orchagent/copporch.h | 32 +- orchagent/orchdaemon.cpp | 9 +- tests/conftest.py | 2 +- tests/test_copp.py | 752 +++++++++++++++++++++++++++++++++++++ 9 files changed, 2207 insertions(+), 328 deletions(-) create mode 100644 cfgmgr/coppmgr.cpp create mode 100644 cfgmgr/coppmgr.h create mode 100644 cfgmgr/coppmgrd.cpp create mode 100644 tests/test_copp.py diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 001c6f1f99e5..119c05ea7d90 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -3,7 +3,7 @@ CFLAGS_SAI = -I /usr/include/sai LIBNL_CFLAGS = -I/usr/include/libnl3 LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 -bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd +bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -60,3 +60,9 @@ natmgrd_SOURCES = natmgrd.cpp natmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_ natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) natmgrd_LDADD = -lswsscommon + +coppmgrd_SOURCES = coppmgrd.cpp coppmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +coppmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +coppmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +coppmgrd_LDADD = -lswsscommon + diff --git a/cfgmgr/coppmgr.cpp b/cfgmgr/coppmgr.cpp new file mode 100644 index 000000000000..834b2c5ff0b2 --- /dev/null +++ b/cfgmgr/coppmgr.cpp @@ -0,0 +1,761 @@ +#include +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "tokenize.h" +#include "ipprefix.h" +#include "coppmgr.h" +#include "exec.h" +#include "shellcmd.h" +#include "warm_restart.h" +#include "json.hpp" + +using json = nlohmann::json; + +using namespace std; +using namespace swss; + +static set g_copp_init_set; + +void CoppMgr::parseInitFile(void) +{ + std::ifstream ifs(COPP_INIT_FILE); + if (ifs.fail()) + { + SWSS_LOG_ERROR("COPP init file %s not found", COPP_INIT_FILE); + return; + } + json j = json::parse(ifs); + for(auto tbl = j.begin(); tbl != j.end(); tbl++) + { + string table_name = tbl.key(); + json keys = tbl.value(); + for (auto k = keys.begin(); k != keys.end(); k++) + { + string table_key = k.key(); + json fvp = k.value(); + vector fvs; + + for (auto f = fvp.begin(); f != fvp.end(); f++) + { + FieldValueTuple fv(f.key(), f.value().get()); + fvs.push_back(fv); + } + if (table_name == CFG_COPP_TRAP_TABLE_NAME) + { + m_coppTrapInitCfg[table_key] = fvs; + } + else if (table_name == CFG_COPP_GROUP_TABLE_NAME) + { + m_coppGroupInitCfg[table_key] = fvs; + } + } + } +} + +/* Check if the trap group has traps that can be installed only when + * feature is enabled + */ +bool CoppMgr::checkTrapGroupPending(string trap_group_name) +{ + bool traps_present = false; + for (auto it: m_coppTrapIdTrapGroupMap) + { + if (it.second == trap_group_name) + { + traps_present = true; + /* At least one trap should be enabled to install the trap group + */ + if (!isTrapIdDisabled(it.first)) + { + return false; + } + } + } + return traps_present; +} + +/* Feature name and CoPP Trap table name must match */ +void CoppMgr::setFeatureTrapIdsStatus(string feature, bool enable) +{ + bool disabled_trap = (m_coppDisabledTraps.find(feature) != m_coppDisabledTraps.end()); + + if ((enable && !disabled_trap) || (!enable && disabled_trap)) + { + return; + } + + if (m_coppTrapConfMap.find(feature) == m_coppTrapConfMap.end()) + { + if (!enable) + { + m_coppDisabledTraps.insert(feature); + } + return; + } + string trap_group = m_coppTrapConfMap[feature].trap_group; + bool prev_group_state = checkTrapGroupPending(trap_group); + + if (!enable) + { + m_coppDisabledTraps.insert(feature); + } + else + { + m_coppDisabledTraps.erase(feature); + } + + /* Trap group moved to pending state when feature is disabled. Remove trap group + */ + if (checkTrapGroupPending(trap_group) && !prev_group_state) + { + m_appCoppTable.del(trap_group); + delCoppGroupStateOk(trap_group); + return; + } + vector fvs; + string trap_ids; + + /* Trap group moved from pending state to enabled state + * Hence install include all fields to create the group + */ + if (prev_group_state && !checkTrapGroupPending(trap_group)) + { + for (auto i : m_coppGroupFvs[trap_group]) + { + FieldValueTuple fv(i.first, i.second); + fvs.push_back(fv); + } + } + getTrapGroupTrapIds(trap_group, trap_ids); + if (!trap_ids.empty()) + { + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + fvs.push_back(fv); + } + if (!fvs.empty()) + { + m_appCoppTable.set(trap_group, fvs); + setCoppGroupStateOk(trap_group); + } +} + +bool CoppMgr::isTrapIdDisabled(string trap_id) +{ + for (auto &m: m_coppDisabledTraps) + { + if (m_coppTrapConfMap.find(m) == m_coppTrapConfMap.end()) + { + continue; + } + vector trap_id_list; + + trap_id_list = tokenize(m_coppTrapConfMap[m].trap_ids, list_item_delimiter); + if(std::find(trap_id_list.begin(), trap_id_list.end(), trap_id) != trap_id_list.end()) + { + return true; + } + + } + return false; +} +void CoppMgr::mergeConfig(CoppCfg &init_cfg, CoppCfg &m_cfg, std::vector &cfg_keys, Table &cfgTable) +{ + /* Read the init configuration first. If the same key is present in + * user configuration, override the init fields with user fields + */ + for (auto i : init_cfg) + { + std::vector init_fvs = i.second; + std::vector merged_fvs; + auto key = std::find(cfg_keys.begin(), cfg_keys.end(), i.first); + bool null_cfg = false; + if (key != cfg_keys.end()) + { + std::vector cfg_fvs; + cfgTable.get(i.first, cfg_fvs); + + merged_fvs = cfg_fvs; + for (auto it1: init_fvs) + { + bool field_found = false; + for (auto it2: cfg_fvs) + { + if(fvField(it2) == "NULL") + { + SWSS_LOG_DEBUG("Ignoring create for key %s",i.first.c_str()); + null_cfg = true; + break; + } + if(fvField(it1) == fvField(it2)) + { + field_found = true; + break; + } + } + if (!field_found) + { + merged_fvs.push_back(it1); + } + } + if (!null_cfg) + { + m_cfg[i.first] = merged_fvs; + } + } + else + { + m_cfg[i.first] = init_cfg[i.first]; + } + } + + /* Read the user configuration keys that were not present in + * init configuration. + */ + for (auto i : cfg_keys) + { + if(init_cfg.find(i) == init_cfg.end()) + { + std::vector cfg_fvs; + cfgTable.get(i, cfg_fvs); + m_cfg[i] = cfg_fvs; + } + } +} + +CoppMgr::CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : + Orch(cfgDb, tableNames), + m_cfgCoppTrapTable(cfgDb, CFG_COPP_TRAP_TABLE_NAME), + m_cfgCoppGroupTable(cfgDb, CFG_COPP_GROUP_TABLE_NAME), + m_cfgFeatureTable(cfgDb, CFG_FEATURE_TABLE_NAME), + m_appCoppTable(appDb, APP_COPP_TABLE_NAME), + m_stateCoppTrapTable(stateDb, STATE_COPP_TRAP_TABLE_NAME), + m_stateCoppGroupTable(stateDb, STATE_COPP_GROUP_TABLE_NAME), + m_coppTable(appDb, APP_COPP_TABLE_NAME) +{ + SWSS_LOG_ENTER(); + parseInitFile(); + std::vector group_keys; + std::vector trap_keys; + std::vector feature_keys; + + std::vector group_cfg_keys; + std::vector trap_cfg_keys; + + CoppCfg group_cfg; + CoppCfg trap_cfg; + + m_cfgCoppGroupTable.getKeys(group_cfg_keys); + m_cfgCoppTrapTable.getKeys(trap_cfg_keys); + m_cfgFeatureTable.getKeys(feature_keys); + + + for (auto i: feature_keys) + { + std::vector feature_fvs; + m_cfgFeatureTable.get(i, feature_fvs); + + for (auto j: feature_fvs) + { + if (fvField(j) == "state" && fvValue(j) == "disabled") + { + m_coppDisabledTraps.insert(i); + } + } + } + + mergeConfig(m_coppTrapInitCfg, trap_cfg, trap_cfg_keys, m_cfgCoppTrapTable); + + for (auto i : trap_cfg) + { + string trap_group; + string trap_ids; + std::vector trap_fvs = i.second; + + for (auto j: trap_fvs) + { + if (fvField(j) == COPP_TRAP_ID_LIST_FIELD) + { + trap_ids = fvValue(j); + } + else if (fvField(j) == COPP_TRAP_GROUP_FIELD) + { + trap_group = fvValue(j); + } + } + if (!trap_group.empty() && !trap_ids.empty()) + { + addTrapIdsToTrapGroup(trap_group, trap_ids); + m_coppTrapConfMap[i.first].trap_group = trap_group; + m_coppTrapConfMap[i.first].trap_ids = trap_ids; + setCoppTrapStateOk(i.first); + } + } + + mergeConfig(m_coppGroupInitCfg, group_cfg, group_cfg_keys, m_cfgCoppGroupTable); + + for (auto i: group_cfg) + { + string trap_ids; + vector trap_group_fvs = i.second; + + for (auto fv: trap_group_fvs) + { + m_coppGroupFvs[i.first][fvField(fv)]= fvValue(fv); + } + if (checkTrapGroupPending(i.first)) + { + continue; + } + + getTrapGroupTrapIds(i.first, trap_ids); + if (!trap_ids.empty()) + { + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + trap_group_fvs.push_back(fv); + } + + if (!trap_group_fvs.empty()) + { + m_appCoppTable.set(i.first, trap_group_fvs); + } + setCoppGroupStateOk(i.first); + auto g_cfg = std::find(group_cfg_keys.begin(), group_cfg_keys.end(), i.first); + if (g_cfg != group_cfg_keys.end()) + { + g_copp_init_set.insert(i.first); + } + } +} + +void CoppMgr::setCoppGroupStateOk(string alias) +{ + FieldValueTuple tuple("state", "ok"); + vector fvs; + fvs.push_back(tuple); + m_stateCoppGroupTable.set(alias, fvs); + SWSS_LOG_NOTICE("Publish %s(ok) to state db", alias.c_str()); +} + +void CoppMgr::delCoppGroupStateOk(string alias) +{ + m_stateCoppGroupTable.del(alias); + SWSS_LOG_NOTICE("Delete %s(ok) from state db", alias.c_str()); +} + +void CoppMgr::setCoppTrapStateOk(string alias) +{ + FieldValueTuple tuple("state", "ok"); + vector fvs; + fvs.push_back(tuple); + m_stateCoppTrapTable.set(alias, fvs); + SWSS_LOG_NOTICE("Publish %s(ok) to state db", alias.c_str()); +} + +void CoppMgr::delCoppTrapStateOk(string alias) +{ + m_stateCoppTrapTable.del(alias); + SWSS_LOG_NOTICE("Delete %s(ok) from state db", alias.c_str()); +} + +void CoppMgr::addTrapIdsToTrapGroup(string trap_group, string trap_ids) +{ + vector trap_id_list; + + trap_id_list = tokenize(trap_ids, list_item_delimiter); + + for (auto i: trap_id_list) + { + m_coppTrapIdTrapGroupMap[i] = trap_group; + } +} + +void CoppMgr::removeTrapIdsFromTrapGroup(string trap_group, string trap_ids) +{ + vector trap_id_list; + + trap_id_list = tokenize(trap_ids, list_item_delimiter); + + for (auto i: trap_id_list) + { + m_coppTrapIdTrapGroupMap.erase(i); + } +} + +void CoppMgr::getTrapGroupTrapIds(string trap_group, string &trap_ids) +{ + + trap_ids.clear(); + for (auto it: m_coppTrapIdTrapGroupMap) + { + if (it.second == trap_group) + { + if (isTrapIdDisabled(it.first)) + { + continue; + } + if (trap_ids.empty()) + { + trap_ids = it.first; + } + else + { + trap_ids += list_item_delimiter + it.first; + } + } + } +} + +void CoppMgr::doCoppTrapTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + vector fvs; + string trap_ids = ""; + string trap_group = ""; + bool conf_present = false; + + if (m_coppTrapConfMap.find(key) != m_coppTrapConfMap.end()) + { + trap_ids = m_coppTrapConfMap[key].trap_ids; + trap_group = m_coppTrapConfMap[key].trap_group; + conf_present = true; + } + + if (op == SET_COMMAND) + { + /*Create case*/ + bool null_cfg = false; + for (auto i: kfvFieldsValues(t)) + { + if (fvField(i) == COPP_TRAP_GROUP_FIELD) + { + trap_group = fvValue(i); + } + else if (fvField(i) == COPP_TRAP_ID_LIST_FIELD) + { + trap_ids = fvValue(i); + } + else if (fvField(i) == "NULL") + { + null_cfg = true; + } + } + if (null_cfg) + { + if (conf_present) + { + SWSS_LOG_DEBUG("Deleting trap key %s", key.c_str()); + removeTrapIdsFromTrapGroup(m_coppTrapConfMap[key].trap_group, + m_coppTrapConfMap[key].trap_ids); + trap_ids.clear(); + setCoppTrapStateOk(key); + getTrapGroupTrapIds(m_coppTrapConfMap[key].trap_group, trap_ids); + fvs.clear(); + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + fvs.push_back(fv); + if (!checkTrapGroupPending(m_coppTrapConfMap[key].trap_group)) + { + m_appCoppTable.set(m_coppTrapConfMap[key].trap_group, fvs); + setCoppGroupStateOk(m_coppTrapConfMap[key].trap_group); + } + m_coppTrapConfMap.erase(key); + } + it = consumer.m_toSync.erase(it); + continue; + } + /*Duplicate check*/ + if (conf_present && + (trap_group == m_coppTrapConfMap[key].trap_group) && + (trap_ids == m_coppTrapConfMap[key].trap_ids)) + { + it = consumer.m_toSync.erase(it); + continue; + } + /* Incomplete configuration. Do not process until both trap group + * and trap_ids are available + */ + if (trap_group.empty() || trap_ids.empty()) + { + it = consumer.m_toSync.erase(it); + continue; + } + /* Remove the current trap IDs and add the new trap IDS to recompute the + * trap IDs for the trap group + */ + if (conf_present) + { + removeTrapIdsFromTrapGroup(m_coppTrapConfMap[key].trap_group, + m_coppTrapConfMap[key].trap_ids); + } + fvs.clear(); + string trap_group_trap_ids; + addTrapIdsToTrapGroup(trap_group, trap_ids); + getTrapGroupTrapIds(trap_group, trap_group_trap_ids); + FieldValueTuple fv1(COPP_TRAP_ID_LIST_FIELD, trap_group_trap_ids); + fvs.push_back(fv1); + if (!checkTrapGroupPending(trap_group)) + { + m_appCoppTable.set(trap_group, fvs); + setCoppGroupStateOk(trap_group); + } + + /* When the trap table's trap group is changed, the old trap group + * should also be reprogrammed as some of its associated traps got + * removed + */ + if (conf_present && (trap_group != m_coppTrapConfMap[key].trap_group)) + { + trap_group_trap_ids.clear(); + fvs.clear(); + getTrapGroupTrapIds(m_coppTrapConfMap[key].trap_group, trap_group_trap_ids); + FieldValueTuple fv2(COPP_TRAP_ID_LIST_FIELD, trap_group_trap_ids); + fvs.push_back(fv2); + if (!checkTrapGroupPending(m_coppTrapConfMap[key].trap_group)) + { + m_appCoppTable.set(m_coppTrapConfMap[key].trap_group, fvs); + setCoppGroupStateOk(m_coppTrapConfMap[key].trap_group); + } + } + m_coppTrapConfMap[key].trap_group = trap_group; + m_coppTrapConfMap[key].trap_ids = trap_ids; + setCoppTrapStateOk(key); + } + else if (op == DEL_COMMAND) + { + if (conf_present) + { + removeTrapIdsFromTrapGroup(m_coppTrapConfMap[key].trap_group, + m_coppTrapConfMap[key].trap_ids); + } + fvs.clear(); + trap_ids.clear(); + if (conf_present && !m_coppTrapConfMap[key].trap_group.empty()) + { + getTrapGroupTrapIds(m_coppTrapConfMap[key].trap_group, trap_ids); + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + fvs.push_back(fv); + if (!checkTrapGroupPending(m_coppTrapConfMap[key].trap_group)) + { + m_appCoppTable.set(m_coppTrapConfMap[key].trap_group, fvs); + setCoppGroupStateOk(m_coppTrapConfMap[key].trap_group); + } + } + if (conf_present) + { + m_coppTrapConfMap.erase(key); + } + delCoppTrapStateOk(key); + + /* If the COPP trap was part of init config, it needs to be recreated + * with field values from init. The configuration delete should just clear + * the externally applied user configuration + */ + if (m_coppTrapInitCfg.find(key) != m_coppTrapInitCfg.end()) + { + auto fvs = m_coppTrapInitCfg[key]; + for (auto i: fvs) + { + if (fvField(i) == COPP_TRAP_GROUP_FIELD) + { + trap_group = fvValue(i); + } + else if (fvField(i) == COPP_TRAP_ID_LIST_FIELD) + { + trap_ids = fvValue(i); + } + } + vector g_fvs; + string trap_group_trap_ids; + addTrapIdsToTrapGroup(trap_group, trap_ids); + getTrapGroupTrapIds(trap_group, trap_group_trap_ids); + FieldValueTuple fv1(COPP_TRAP_ID_LIST_FIELD, trap_group_trap_ids); + g_fvs.push_back(fv1); + if (!checkTrapGroupPending(trap_group)) + { + m_appCoppTable.set(trap_group, g_fvs); + setCoppGroupStateOk(trap_group); + } + m_coppTrapConfMap[key].trap_group = trap_group; + m_coppTrapConfMap[key].trap_ids = trap_ids; + setCoppTrapStateOk(key); + } + } + it = consumer.m_toSync.erase(it); + } +} + +/* This API is used to fetch only the modified configurations + */ +void CoppMgr::coppGroupGetModifiedFvs(string key, vector &trap_group_fvs, + vector &modified_fvs) +{ + modified_fvs = trap_group_fvs; + + if (m_coppGroupFvs.find(key) == m_coppGroupFvs.end()) + { + return; + } + + for (auto fv: trap_group_fvs) + { + if(fvField(fv) == COPP_TRAP_ID_LIST_FIELD || + m_coppGroupFvs[key].find(fvField(fv)) == m_coppGroupFvs[key].end()) + { + continue; + } + + if (m_coppGroupFvs[key][fvField(fv)] == fvValue(fv)) + { + auto vec_idx = std::find(modified_fvs.begin(), modified_fvs.end(), fv); + modified_fvs.erase(vec_idx); + } + } +} + + +void CoppMgr::doCoppGroupTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + auto fvs = kfvFieldsValues(t); + string trap_ids; + vector modified_fvs; + + if (op == SET_COMMAND) + { + if (g_copp_init_set.find(key) != g_copp_init_set.end()) + { + g_copp_init_set.erase(key); + it = consumer.m_toSync.erase(it); + continue; + } + + getTrapGroupTrapIds(key, trap_ids); + if (!trap_ids.empty()) + { + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + fvs.push_back(fv); + } + + coppGroupGetModifiedFvs(key, fvs, modified_fvs); + if (!checkTrapGroupPending(key)) + { + if (!modified_fvs.empty()) + { + m_appCoppTable.set(key, modified_fvs); + setCoppGroupStateOk(key); + } + } + for (auto fv: fvs) + { + if(fvField(fv) != COPP_TRAP_ID_LIST_FIELD) + { + m_coppGroupFvs[key][fvField(fv)] = fvValue(fv); + } + } + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_NOTICE("%s: DEL",key.c_str()); + if (!checkTrapGroupPending(key)) + { + m_appCoppTable.del(key); + delCoppGroupStateOk(key); + } + + /* If the COPP group was part of init config, it needs to be recreated + * with field values from init. The configuration delete should just clear + * the externally applied user configuration + */ + if (m_coppGroupInitCfg.find(key) != m_coppGroupInitCfg.end()) + { + std::vector fvs = m_coppGroupInitCfg[key]; + for (auto fv: fvs) + { + m_coppGroupFvs[key][fvField(fv)] = fvValue(fv); + } + if (!checkTrapGroupPending(key)) + { + getTrapGroupTrapIds(key, trap_ids); + if (!trap_ids.empty()) + { + FieldValueTuple fv(COPP_TRAP_ID_LIST_FIELD, trap_ids); + fvs.push_back(fv); + } + m_appCoppTable.set(key, fvs); + setCoppGroupStateOk(key); + } + } + else + { + m_coppGroupFvs.erase(key); + } + } + it = consumer.m_toSync.erase(it); + } +} + +void CoppMgr::doFeatureTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + vector fvs; + string trap_ids; + + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + { + bool status = false; + if (fvValue(i) == "enabled") + { + status = true; + } + setFeatureTrapIdsStatus(key, status); + } + } + } + else if (op == DEL_COMMAND) + { + setFeatureTrapIdsStatus(key, false); + } + it = consumer.m_toSync.erase(it); + } +} + +void CoppMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto table = consumer.getTableName(); + + if (table == CFG_COPP_TRAP_TABLE_NAME) + { + doCoppTrapTask(consumer); + } + else if (table == CFG_COPP_GROUP_TABLE_NAME) + { + doCoppGroupTask(consumer); + } + else if (table == CFG_FEATURE_TABLE_NAME) + { + doFeatureTask(consumer); + } +} diff --git a/cfgmgr/coppmgr.h b/cfgmgr/coppmgr.h new file mode 100644 index 000000000000..b010489f2eee --- /dev/null +++ b/cfgmgr/coppmgr.h @@ -0,0 +1,103 @@ +#pragma once + +#include "dbconnector.h" +#include "orch.h" +#include "producerstatetable.h" +#include + +#include +#include +#include + +namespace swss { + +/* COPP Trap Table Fields */ +#define COPP_TRAP_ID_LIST_FIELD "trap_ids" +#define COPP_TRAP_GROUP_FIELD "trap_group" + +/* COPP Group Table Fields */ +#define COPP_GROUP_QUEUE_FIELD "queue" +#define COPP_GROUP_TRAP_ACTION_FIELD "trap_action" +#define COPP_GROUP_TRAP_PRIORITY_FIELD "trap_priority" +#define COPP_GROUP_POLICER_METER_TYPE_FIELD "meter_type" +#define COPP_GROUP_POLICER_MODE_FIELD "mode" +#define COPP_GROUP_POLICER_COLOR_FIELD "color" +#define COPP_GROUP_POLICER_CBS_FIELD "cbs" +#define COPP_GROUP_POLICER_CIR_FIELD "cir" +#define COPP_GROUP_POLICER_PBS_FIELD "pbs" +#define COPP_GROUP_POLICER_PIR_FIELD "pir" +#define COPP_GROUP_POLICER_ACTION_GREEN_FIELD "green_action" +#define COPP_GROUP_POLICER_ACTION_RED_FIELD "red_action" +#define COPP_GROUP_POLICER_ACTION_YELLOW_FIELD "yellow_action" + +/* sflow genetlink fields */ +#define COPP_GROUP_GENETLINK_NAME_FIELD "genetlink_name" +#define COPP_GROUP_GENETLINK_MCGRP_NAME_FIELD "genetlink_mcgrp_name" + +#define COPP_TRAP_TYPE_SAMPLEPACKET "sample_packet" + +#define COPP_INIT_FILE "/etc/sonic/copp_cfg.json" + +struct CoppTrapConf +{ + std::string trap_ids; + std::string trap_group; +}; + +/* TrapName to TrapConf map */ +typedef std::map CoppTrapConfMap; + +/* TrapID to Trap group name map */ +typedef std::map CoppTrapIdTrapGroupMap; + +/* Key to Field value Tuple map */ +typedef std::map> CoppCfg; + +/* Restricted Copp group key to Field value map's map */ +typedef std::map> CoppGroupFvs; + +class CoppMgr : public Orch +{ +public: + CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, + const std::vector &tableNames); + + using Orch::doTask; +private: + Table m_cfgCoppTrapTable, m_cfgCoppGroupTable, m_cfgFeatureTable, m_coppTable; + Table m_stateCoppTrapTable, m_stateCoppGroupTable; + ProducerStateTable m_appCoppTable; + CoppTrapConfMap m_coppTrapConfMap; + CoppTrapIdTrapGroupMap m_coppTrapIdTrapGroupMap; + CoppGroupFvs m_coppGroupFvs; + std::set m_coppDisabledTraps; + CoppCfg m_coppGroupInitCfg; + CoppCfg m_coppTrapInitCfg; + + + void doTask(Consumer &consumer); + void doCoppGroupTask(Consumer &consumer); + void doCoppTrapTask(Consumer &consumer); + void doFeatureTask(Consumer &consumer); + + void getTrapGroupTrapIds(std::string trap_group, std::string &trap_ids); + void removeTrapIdsFromTrapGroup(std::string trap_group, std::string trap_ids); + void addTrapIdsToTrapGroup(std::string trap_group, std::string trap_ids); + bool isTrapIdDisabled(std::string trap_id); + void setFeatureTrapIdsStatus(std::string feature, bool enable); + bool checkTrapGroupPending(std::string trap_group_name); + + void setCoppGroupStateOk(std::string alias); + void delCoppGroupStateOk(std::string alias); + + void setCoppTrapStateOk(std::string alias); + void delCoppTrapStateOk(std::string alias); + void coppGroupGetModifiedFvs(std::string key, std::vector &trap_group_fvs, + std::vector &modified_fvs); + void parseInitFile(void); + bool isTrapGroupInstalled(std::string key); + void mergeConfig(CoppCfg &init_cfg, CoppCfg &m_cfg, std::vector &cfg_keys, Table &cfgTable); + +}; + +} diff --git a/cfgmgr/coppmgrd.cpp b/cfgmgr/coppmgrd.cpp new file mode 100644 index 000000000000..1995405cd64f --- /dev/null +++ b/cfgmgr/coppmgrd.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#include "exec.h" +#include "coppmgr.h" +#include "schema.h" +#include "select.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +/* select() function timeout retry time, in millisecond */ +#define SELECT_TIMEOUT 1000 + +/* + * Following global variables are defined here for the purpose of + * using existing Orch class which is to be refactored soon to + * eliminate the direct exposure of the global variables. + * + * Once Orch class refactoring is done, these global variables + * should be removed from here. + */ +int gBatchSize = 0; +bool gSwssRecord = false; +bool gLogRotate = false; +ofstream gRecordOfs; +string gRecordFile; +/* Global database mutex */ +mutex gDbMutex; + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("coppmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting coppmgrd ---"); + + try + { + vector cfg_copp_tables = { + CFG_COPP_TRAP_TABLE_NAME, + CFG_COPP_GROUP_TABLE_NAME, + CFG_FEATURE_TABLE_NAME, + }; + + WarmStart::initialize("coppmgrd", "swss"); + WarmStart::checkWarmStart("coppmgrd", "swss"); + + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); + + CoppMgr coppmgr(&cfgDb, &appDb, &stateDb, cfg_copp_tables); + + vector cfgOrchList = {&coppmgr}; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + coppmgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch (const exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +} diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index 00da16740272..fa3f82d746d6 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -94,15 +94,14 @@ const vector default_trap_ids = { SAI_HOSTIF_TRAP_TYPE_TTL_ERROR }; -CoppOrch::CoppOrch(vector &tableConnectors) : - Orch( tableConnectors) +CoppOrch::CoppOrch(DBConnector* db, string tableName) : + Orch(db, tableName) { SWSS_LOG_ENTER(); initDefaultHostIntfTable(); initDefaultTrapGroup(); initDefaultTrapIds(); - enable_sflow_trap = false; }; void CoppOrch::initDefaultHostIntfTable() @@ -203,14 +202,10 @@ void CoppOrch::getTrapIdList(vector &trap_id_name_list, vector &trap_id_name_list) +bool CoppOrch::createGenetlinkHostIfTable(vector &trap_id_list) { SWSS_LOG_ENTER(); - vector trap_id_list; - - getTrapIdList(trap_id_name_list, trap_id_list); - for (auto trap_id : trap_id_list) { auto host_tbl_entry = m_trapid_hostif_table_map.find(trap_id); @@ -257,6 +252,28 @@ bool CoppOrch::createGenetlinkHostIfTable(vector &trap_id_name_list) return true; } +bool CoppOrch::removeGenetlinkHostIfTable(vector &trap_id_list) +{ + sai_status_t sai_status; + + for (auto trap_id : trap_id_list) + { + if ( m_trapid_hostif_table_map.find(trap_id) != m_trapid_hostif_table_map.end()) + { + sai_status = sai_hostif_api->remove_hostif_table_entry( + m_trapid_hostif_table_map[trap_id]); + if(sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete hostif table entry %" PRId64 " \ + rc=%d", m_trapid_hostif_table_map[trap_id], sai_status); + return false; + } + m_trapid_hostif_table_map.erase(trap_id); + } + } + return true; + +} bool CoppOrch::applyAttributesToTrapIds(sai_object_id_t trap_group_id, const vector &trap_id_list, vector &trap_id_attribs) @@ -285,23 +302,6 @@ bool CoppOrch::applyAttributesToTrapIds(sai_object_id_t trap_group_id, return true; } -bool CoppOrch::applyTrapIds(sai_object_id_t trap_group, vector &trap_id_name_list, vector &trap_id_attribs) -{ - SWSS_LOG_ENTER(); - - vector trap_id_list; - - getTrapIdList(trap_id_name_list, trap_id_list); - - sai_attribute_t attr; - attr.id = SAI_HOSTIF_TRAP_ATTR_TRAP_GROUP; - attr.value.oid = trap_group; - trap_id_attribs.push_back(attr); - - return applyAttributesToTrapIds(trap_group, trap_id_list, trap_id_attribs); -} - - bool CoppOrch::removePolicer(string trap_group_name) { SWSS_LOG_ENTER(); @@ -414,25 +414,12 @@ bool CoppOrch::removeGenetlinkHostIf(string trap_group_name) SWSS_LOG_ENTER(); sai_status_t sai_status; + vector group_trap_ids; - for (auto it : m_syncdTrapIds) + getTrapIdsFromTrapGroup (m_trap_group_map[trap_group_name], group_trap_ids); + if (!removeGenetlinkHostIfTable(group_trap_ids)) { - if (it.second.trap_group_obj == m_trap_group_map[trap_group_name]) - { - auto hostTableEntry = m_trapid_hostif_table_map.find(it.first); - if (hostTableEntry != m_trapid_hostif_table_map.end()) - { - sai_status = sai_hostif_api->remove_hostif_table_entry(hostTableEntry->second); - if(sai_status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to delete hostif table entry %" PRId64 " \ - on trap group %s. rc=%d", hostTableEntry->second, - trap_group_name.c_str(), sai_status); - return false; - } - m_trapid_hostif_table_map.erase(it.first); - } - } + return false; } auto hostInfo = m_trap_group_hostif_map.find(m_trap_group_map[trap_group_name]); @@ -468,180 +455,32 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) vector policer_attribs; vector genetlink_attribs; + vector trap_ids; + vector add_trap_ids; + vector rem_trap_ids; + std::vector fv_tuple = kfvFieldsValues(tuple); + if (op == SET_COMMAND) { - for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + for (auto i = fv_tuple.begin(); i != fv_tuple.end(); i++) { - sai_attribute_t attr; - if (fvField(*i) == copp_trap_id_list) { trap_id_list = tokenize(fvValue(*i), list_item_delimiter); - auto it = std::find(trap_id_list.begin(), trap_id_list.end(), "sample_packet"); - if (it != trap_id_list.end()) - { - if (!enable_sflow_trap) - { - return task_process_status::task_need_retry; - } - } - } - else if (fvField(*i) == copp_queue_field) - { - attr.id = SAI_HOSTIF_TRAP_GROUP_ATTR_QUEUE; - attr.value.u32 = (uint32_t)stoul(fvValue(*i)); - trap_gr_attribs.push_back(attr); - } - // - // Trap related attributes - // - else if (fvField(*i) == copp_trap_action_field) - { - sai_packet_action_t trap_action = packet_action_map.at(fvValue(*i)); - attr.id = SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION; - attr.value.s32 = trap_action; - trap_id_attribs.push_back(attr); - } - else if (fvField(*i) == copp_trap_priority_field) - { - /* Mellanox platform doesn't support trap priority setting */ - /* Marvell platform doesn't support trap priority. */ - char *platform = getenv("platform"); - if (!platform || (!strstr(platform, MLNX_PLATFORM_SUBSTRING) && (!strstr(platform, MRVL_PLATFORM_SUBSTRING)))) - { - attr.id = SAI_HOSTIF_TRAP_ATTR_TRAP_PRIORITY, - attr.value.u32 = (uint32_t)stoul(fvValue(*i)); - trap_id_attribs.push_back(attr); - } - } - // - // process policer attributes - // - else if (fvField(*i) == copp_policer_meter_type_field) - { - sai_meter_type_t meter_value = policer_meter_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_METER_TYPE; - attr.value.s32 = meter_value; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_mode_field) - { - sai_policer_mode_t mode = policer_mode_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_MODE; - attr.value.s32 = mode; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_color_field) - { - sai_policer_color_source_t color = policer_color_aware_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_COLOR_SOURCE; - attr.value.s32 = color; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_cbs_field) - { - attr.id = SAI_POLICER_ATTR_CBS; - attr.value.u64 = stoul(fvValue(*i)); - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_cir_field) - { - attr.id = SAI_POLICER_ATTR_CIR; - attr.value.u64 = stoul(fvValue(*i)); - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_pbs_field) - { - attr.id = SAI_POLICER_ATTR_PBS; - attr.value.u64 = stoul(fvValue(*i)); - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_pir_field) - { - attr.id = SAI_POLICER_ATTR_PIR; - attr.value.u64 = stoul(fvValue(*i)); - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_action_green_field) - { - sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_GREEN_PACKET_ACTION; - attr.value.s32 = policer_action; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_action_red_field) - { - sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; - attr.value.s32 = policer_action; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_policer_action_yellow_field) - { - sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); - attr.id = SAI_POLICER_ATTR_YELLOW_PACKET_ACTION; - attr.value.s32 = policer_action; - policer_attribs.push_back(attr); - } - else if (fvField(*i) == copp_genetlink_name) - { - attr.id = SAI_HOSTIF_ATTR_TYPE; - attr.value.s32 = SAI_HOSTIF_TYPE_GENETLINK; - genetlink_attribs.push_back(attr); - - attr.id = SAI_HOSTIF_ATTR_NAME; - strncpy(attr.value.chardata, fvValue(*i).c_str(), - sizeof(attr.value.chardata)); - genetlink_attribs.push_back(attr); - - } - else if (fvField(*i) == copp_genetlink_mcgrp_name) - { - attr.id = SAI_HOSTIF_ATTR_GENETLINK_MCGRP_NAME; - strncpy(attr.value.chardata, fvValue(*i).c_str(), - sizeof(attr.value.chardata)); - genetlink_attribs.push_back(attr); - } - else - { - SWSS_LOG_ERROR("Unknown copp field specified:%s\n", fvField(*i).c_str()); - return task_process_status::task_invalid_entry; + getTrapIdList(trap_id_list, trap_ids); + getTrapAddandRemoveList(trap_group_name, trap_ids, add_trap_ids, rem_trap_ids); } } + if (!getAttribsFromTrapGroup(fv_tuple, trap_gr_attribs, trap_id_attribs, + policer_attribs, genetlink_attribs)) + { + return task_process_status::task_invalid_entry; + } + /* Set host interface trap group */ if (m_trap_group_map.find(trap_group_name) != m_trap_group_map.end()) { - /* Create or set policer */ - if (!policer_attribs.empty()) - { - sai_object_id_t policer_id = getPolicer(trap_group_name); - if (SAI_NULL_OBJECT_ID == policer_id) - { - SWSS_LOG_WARN("Creating policer for existing Trap group:%" PRIx64 " (name:%s).", m_trap_group_map[trap_group_name], trap_group_name.c_str()); - if (!createPolicer(trap_group_name, policer_attribs)) - { - return task_process_status::task_failed; - } - SWSS_LOG_DEBUG("Created policer:%" PRIx64 " for existing trap group", policer_id); - } - else - { - /* TODO: We should really only set changed attributes. - The changes need to detected either by orch agent submodule or by the orch agent framework. */ - for (sai_uint32_t ind = 0; ind < policer_attribs.size(); ind++) - { - auto policer_attr = policer_attribs[ind]; - sai_status = sai_policer_api->set_policer_attribute(policer_id, &policer_attr); - if (sai_status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to apply attribute[%d].id=%d to policer for trap group:%s, error:%d\n", ind, policer_attr.id, trap_group_name.c_str(), sai_status); - return task_process_status::task_failed; - } - } - } - } - for (sai_uint32_t ind = 0; ind < trap_gr_attribs.size(); ind++) { auto trap_gr_attr = trap_gr_attribs[ind]; @@ -669,54 +508,69 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) SWSS_LOG_NOTICE("Create host interface trap group %s", trap_group_name.c_str()); m_trap_group_map[trap_group_name] = new_trap; + } - /* Create policer */ - if (!policer_attribs.empty()) + if (!policer_attribs.empty()) + { + if (!trapGroupUpdatePolicer(trap_group_name, policer_attribs)) { - if (!createPolicer(trap_group_name, policer_attribs)) - { - return task_process_status::task_failed; - } + return task_process_status::task_failed; } - - if (!genetlink_attribs.empty()) + } + if (!trap_id_attribs.empty()) + { + vector group_trap_ids; + TrapIdAttribs trap_attr; + getTrapIdsFromTrapGroup(m_trap_group_map[trap_group_name], + group_trap_ids); + for (auto trap_id : group_trap_ids) { - if (!createGenetlinkHostIf(trap_group_name, genetlink_attribs)) + for (auto i: trap_id_attribs) { - return task_process_status::task_failed; + sai_status = sai_hostif_api->set_hostif_trap_attribute( + m_syncdTrapIds[trap_id].trap_obj, &i); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set attribute %d on trap %" PRIx64 "" + " on group %s", i.id, m_syncdTrapIds[trap_id].trap_obj, + trap_group_name.c_str()); + return task_process_status::task_failed; + } } } + for (auto i: trap_id_attribs) + { + trap_attr[i.id] = i.value; + } + m_trap_group_trap_id_attrs[trap_group_name] = trap_attr; } - - /* Apply traps to trap group */ - if (!applyTrapIds(m_trap_group_map[trap_group_name], trap_id_list, trap_id_attribs)) - { - return task_process_status::task_failed; - } - if (!genetlink_attribs.empty()) { - if (!createGenetlinkHostIfTable(trap_id_list)) + if (m_trap_group_hostif_map.find(m_trap_group_map[trap_group_name]) != + m_trap_group_hostif_map.end()) + { + SWSS_LOG_ERROR("Genetlink hostif exists for the trap group %s", + trap_group_name.c_str()); + return task_process_status::task_failed; + } + vector genetlink_trap_ids; + getTrapIdsFromTrapGroup(m_trap_group_map[trap_group_name], genetlink_trap_ids); + if (!createGenetlinkHostIf(trap_group_name, genetlink_attribs)) + { + return task_process_status::task_failed; + } + if (!createGenetlinkHostIfTable(genetlink_trap_ids)) { return task_process_status::task_failed; } } - } - else if (op == DEL_COMMAND) - { - /* Remove policer if any */ - if (!removePolicer(trap_group_name)) - { - SWSS_LOG_ERROR("Failed to remove policer from trap group %s", trap_group_name.c_str()); - return task_process_status::task_failed; - } - - if (!removeGenetlinkHostIf(trap_group_name)) + if (!trapGroupProcessTrapIdChange(trap_group_name, add_trap_ids, rem_trap_ids)) { - SWSS_LOG_ERROR("Failed to remove hostif from trap group %s", trap_group_name.c_str()); return task_process_status::task_failed; } - + } + else if (op == DEL_COMMAND) + { /* Do not remove default trap group */ if (trap_group_name == default_trap_group) { @@ -724,48 +578,10 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) return task_process_status::task_ignore; } - /* Reset the trap IDs to default trap group with default attributes */ - vector trap_ids_to_reset; - for (auto it : m_syncdTrapIds) - { - if (it.second.trap_group_obj == m_trap_group_map[trap_group_name]) - { - trap_ids_to_reset.push_back(it.first); - sai_status = sai_hostif_api->remove_hostif_trap(it.second.trap_obj); - if (sai_status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", it.second.trap_obj); - return task_process_status::task_failed; - } - } - } - - sai_attribute_t attr; - vector default_trap_attrs; - - attr.id = SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION; - attr.value.s32 = SAI_PACKET_ACTION_FORWARD; - default_trap_attrs.push_back(attr); - - attr.id = SAI_HOSTIF_TRAP_ATTR_TRAP_GROUP; - attr.value.oid = m_trap_group_map[default_trap_group]; - default_trap_attrs.push_back(attr); - - if (!applyAttributesToTrapIds(m_trap_group_map[default_trap_group], trap_ids_to_reset, default_trap_attrs)) - { - SWSS_LOG_ERROR("Failed to reset traps to default trap group with default attributes"); - return task_process_status::task_failed; - } - - sai_status = sai_hostif_api->remove_hostif_trap_group(m_trap_group_map[trap_group_name]); - if (sai_status != SAI_STATUS_SUCCESS) + if (!processTrapGroupDel(trap_group_name)) { - SWSS_LOG_ERROR("Failed to remove trap group %s", trap_group_name.c_str()); return task_process_status::task_failed; } - - auto it_del = m_trap_group_map.find(trap_group_name); - m_trap_group_map.erase(it_del); } else { @@ -775,50 +591,11 @@ task_process_status CoppOrch::processCoppRule(Consumer& consumer) return task_process_status::task_success; } -/* Program Sflow trap once we get sflow enable command */ -void CoppOrch::coppProcessSflow(Consumer &consumer) -{ - auto it = consumer.m_toSync.begin(); - - while (it != consumer.m_toSync.end()) - { - auto tuple = it->second; - string op = kfvOp(tuple); - - /* - * Need to handled just 'config sflow enable' command to install the sflow trap group - * for the first time to ensure support of genetlink attributes. Rest of the fields or - * disable value or DEL command are not required to be handled - * - */ - if (op == SET_COMMAND) - { - for (auto i : kfvFieldsValues(tuple)) - { - if (fvField(i) == "admin_state") - { - if (fvValue(i) == "up") - { - enable_sflow_trap = true; - } - } - } - } - it = consumer.m_toSync.erase(it); - } -} - void CoppOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); string table_name = consumer.getTableName(); - if (table_name == CFG_SFLOW_TABLE_NAME) - { - coppProcessSflow(consumer); - return; - } - if (!gPortsOrch->allPortsReady()) { return; @@ -869,3 +646,375 @@ void CoppOrch::doTask(Consumer &consumer) } } } + +void CoppOrch::getTrapAddandRemoveList(string trap_group_name, + vector &trap_ids, + vector &add_trap_ids, + vector &rem_trap_ids) +{ + + vector tmp_trap_ids = trap_ids; + if(m_trap_group_map.find(trap_group_name) == m_trap_group_map.end()) + { + add_trap_ids = trap_ids; + rem_trap_ids.clear(); + return; + } + + + for (auto it : m_syncdTrapIds) + { + if (it.second.trap_group_obj == m_trap_group_map[trap_group_name]) + { + /* If new trap list contains already mapped ID remove it */ + auto i = std::find(std::begin(tmp_trap_ids), std::end(tmp_trap_ids), it.first); + + if (i != std::end(tmp_trap_ids)) + { + tmp_trap_ids.erase(i); + } + /* The mapped Trap ID is not found on newly set list and to be removed*/ + else + { + if ((trap_group_name != default_trap_group) || + ((trap_group_name == default_trap_group) && + (it.first != SAI_HOSTIF_TRAP_TYPE_TTL_ERROR))) + { + rem_trap_ids.push_back(it.first); + } + } + } + } + + add_trap_ids = tmp_trap_ids; +} + +void CoppOrch::getTrapIdsFromTrapGroup (sai_object_id_t trap_group_obj, + vector &trap_ids) +{ + for(auto it: m_syncdTrapIds) + { + if (it.second.trap_group_obj == trap_group_obj) + { + trap_ids.push_back(it.first); + } + } +} + +bool CoppOrch::trapGroupProcessTrapIdChange (string trap_group_name, + vector &add_trap_ids, + vector &rem_trap_ids) +{ + if (!add_trap_ids.empty()) + { + sai_attribute_t attr; + vector add_trap_attr; + attr.id = SAI_HOSTIF_TRAP_ATTR_TRAP_GROUP; + attr.value.oid = m_trap_group_map[trap_group_name]; + + add_trap_attr.push_back(attr); + + for(auto i: add_trap_ids) + { + if (m_syncdTrapIds.find(i)!= m_syncdTrapIds.end()) + { + sai_status_t sai_status = sai_hostif_api->remove_hostif_trap( + m_syncdTrapIds[i].trap_obj); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", + m_syncdTrapIds[i].trap_obj); + return false; + } + } + } + + for (auto it: m_trap_group_trap_id_attrs[trap_group_name]) + { + attr.id = it.first; + attr.value = it.second; + add_trap_attr.push_back(attr); + } + if (!applyAttributesToTrapIds(m_trap_group_map[trap_group_name], add_trap_ids, + add_trap_attr)) + { + SWSS_LOG_ERROR("Failed to set traps to trap group %s", trap_group_name.c_str()); + return false; + } + if (m_trap_group_hostif_map.find(m_trap_group_map[trap_group_name]) != + m_trap_group_hostif_map.end()) + { + if (!createGenetlinkHostIfTable(add_trap_ids)) + { + return false; + } + } + } + if (!rem_trap_ids.empty()) + { + for (auto i: rem_trap_ids) + { + if (m_syncdTrapIds.find(i)!= m_syncdTrapIds.end()) + { + /* + * A trap ID will be present in rem_trap_id in two scenarios + * 1) When trap group for a trap ID is changed + * 2) When trap ID is completely removed + * In case 1 the first call would be to add the trap ids to a different + * group. This would result in changing the mapping of trap id to trap group + * In case 2 the mapping will remain the same. In this case the trap + * object needs to be deleted + */ + if (m_syncdTrapIds[i].trap_group_obj == m_trap_group_map[trap_group_name]) + { + sai_status_t sai_status = sai_hostif_api->remove_hostif_trap( + m_syncdTrapIds[i].trap_obj); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", + m_syncdTrapIds[i].trap_obj); + return false; + } + m_syncdTrapIds.erase(i); + } + } + } + if (!removeGenetlinkHostIfTable(rem_trap_ids)) + { + return false; + } + } + return true; +} + +bool CoppOrch::processTrapGroupDel (string trap_group_name) +{ + auto it_del = m_trap_group_map.find(trap_group_name); + + if (it_del == m_trap_group_map.end()) + { + return true; + } + /* Remove policer if any */ + if (!removePolicer(trap_group_name)) + { + SWSS_LOG_ERROR("Failed to remove policer from trap group %s", trap_group_name.c_str()); + return false; + } + + if (!removeGenetlinkHostIf(trap_group_name)) + { + SWSS_LOG_ERROR("Failed to remove hostif from trap group %s", trap_group_name.c_str()); + return false; + } + + /* Reset the trap IDs to default trap group with default attributes */ + vector trap_ids_to_reset; + for (auto it : m_syncdTrapIds) + { + if (it.second.trap_group_obj == m_trap_group_map[trap_group_name]) + { + trap_ids_to_reset.push_back(it.first); + sai_status_t sai_status = sai_hostif_api->remove_hostif_trap(it.second.trap_obj); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", it.second.trap_obj); + return false; + } + } + } + + for (auto it: trap_ids_to_reset) + { + m_syncdTrapIds.erase(it); + } + + sai_status_t sai_status = sai_hostif_api->remove_hostif_trap_group( + m_trap_group_map[trap_group_name]); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove trap group %s", trap_group_name.c_str()); + return false; + } + + m_trap_group_map.erase(it_del); + return true; +} + +bool CoppOrch::getAttribsFromTrapGroup (vector &fv_tuple, + vector &trap_gr_attribs, + vector &trap_id_attribs, + vector &policer_attribs, + vector &genetlink_attribs) +{ + sai_attribute_t attr; + + for (auto i = fv_tuple.begin(); i != fv_tuple.end(); i++) + { + if (fvField(*i) == copp_trap_id_list) + { + continue; + } + else if (fvField(*i) == copp_queue_field) + { + attr.id = SAI_HOSTIF_TRAP_GROUP_ATTR_QUEUE; + attr.value.u32 = (uint32_t)stoul(fvValue(*i)); + trap_gr_attribs.push_back(attr); + } + // + // Trap related attributes + // + else if (fvField(*i) == copp_trap_action_field) + { + sai_packet_action_t trap_action = packet_action_map.at(fvValue(*i)); + attr.id = SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION; + attr.value.s32 = trap_action; + trap_id_attribs.push_back(attr); + } + else if (fvField(*i) == copp_trap_priority_field) + { + /* Mellanox platform doesn't support trap priority setting */ + /* Marvell platform doesn't support trap priority. */ + char *platform = getenv("platform"); + if (!platform || (!strstr(platform, MLNX_PLATFORM_SUBSTRING) && (!strstr(platform, MRVL_PLATFORM_SUBSTRING)))) + { + attr.id = SAI_HOSTIF_TRAP_ATTR_TRAP_PRIORITY, + attr.value.u32 = (uint32_t)stoul(fvValue(*i)); + trap_id_attribs.push_back(attr); + } + } + // + // process policer attributes + // + else if (fvField(*i) == copp_policer_meter_type_field) + { + sai_meter_type_t meter_value = policer_meter_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_METER_TYPE; + attr.value.s32 = meter_value; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_mode_field) + { + sai_policer_mode_t mode = policer_mode_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_MODE; + attr.value.s32 = mode; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_color_field) + { + sai_policer_color_source_t color = policer_color_aware_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_COLOR_SOURCE; + attr.value.s32 = color; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_cbs_field) + { + attr.id = SAI_POLICER_ATTR_CBS; + attr.value.u64 = stoul(fvValue(*i)); + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_cir_field) + { + attr.id = SAI_POLICER_ATTR_CIR; + attr.value.u64 = stoul(fvValue(*i)); + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_pbs_field) + { + attr.id = SAI_POLICER_ATTR_PBS; + attr.value.u64 = stoul(fvValue(*i)); + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_pir_field) + { + attr.id = SAI_POLICER_ATTR_PIR; + attr.value.u64 = stoul(fvValue(*i)); + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_action_green_field) + { + sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_GREEN_PACKET_ACTION; + attr.value.s32 = policer_action; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_action_red_field) + { + sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; + attr.value.s32 = policer_action; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_policer_action_yellow_field) + { + sai_packet_action_t policer_action = packet_action_map.at(fvValue(*i)); + attr.id = SAI_POLICER_ATTR_YELLOW_PACKET_ACTION; + attr.value.s32 = policer_action; + policer_attribs.push_back(attr); + } + else if (fvField(*i) == copp_genetlink_name) + { + attr.id = SAI_HOSTIF_ATTR_TYPE; + attr.value.s32 = SAI_HOSTIF_TYPE_GENETLINK; + genetlink_attribs.push_back(attr); + + attr.id = SAI_HOSTIF_ATTR_NAME; + strncpy(attr.value.chardata, fvValue(*i).c_str(), + sizeof(attr.value.chardata)); + genetlink_attribs.push_back(attr); + + } + else if (fvField(*i) == copp_genetlink_mcgrp_name) + { + attr.id = SAI_HOSTIF_ATTR_GENETLINK_MCGRP_NAME; + strncpy(attr.value.chardata, fvValue(*i).c_str(), + sizeof(attr.value.chardata)); + genetlink_attribs.push_back(attr); + } + else + { + SWSS_LOG_ERROR("Unknown copp field specified:%s\n", fvField(*i).c_str()); + return false; + } + } + return true; +} + +bool CoppOrch::trapGroupUpdatePolicer (string trap_group_name, + vector &policer_attribs) +{ + sai_object_id_t policer_id = getPolicer(trap_group_name); + + if (m_trap_group_map.find(trap_group_name) == m_trap_group_map.end()) + { + return false; + } + if (SAI_NULL_OBJECT_ID == policer_id) + { + SWSS_LOG_WARN("Creating policer for existing Trap group: %" PRIx64 " (name:%s).", + m_trap_group_map[trap_group_name], trap_group_name.c_str()); + if (!createPolicer(trap_group_name, policer_attribs)) + { + return false; + } + SWSS_LOG_DEBUG("Created policer:%" PRIx64 " for existing trap group", policer_id); + } + else + { + for (sai_uint32_t ind = 0; ind < policer_attribs.size(); ind++) + { + auto policer_attr = policer_attribs[ind]; + sai_status_t sai_status = sai_policer_api->set_policer_attribute(policer_id, + &policer_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to apply attribute[%d].id=%d to policer for trap group:" + "%s, error:%d\n",ind, policer_attr.id, trap_group_name.c_str(), + sai_status); + return false; + } + } + } + return true; +} + diff --git a/orchagent/copporch.h b/orchagent/copporch.h index c46ac6e62ecd..4794cfd2a648 100644 --- a/orchagent/copporch.h +++ b/orchagent/copporch.h @@ -28,6 +28,7 @@ const std::string copp_policer_action_yellow_field = "yellow_action"; const std::string copp_genetlink_name = "genetlink_name"; const std::string copp_genetlink_mcgrp_name = "genetlink_mcgrp_name"; +typedef std::map TrapIdAttribs; struct copp_trap_objects { sai_object_id_t trap_obj; @@ -42,20 +43,22 @@ typedef std::map TrapIdTrapObjectsTab typedef std::map TrapGroupHostIfMap; /* TrapIdHostIfTableMap: trap type, host table entry ID*/ typedef std::map TrapIdHostIfTableMap; +/* Trap group to trap ID attributes */ +typedef std::map TrapGroupTrapIdAttribs; class CoppOrch : public Orch { public: - CoppOrch(std::vector &tableConnectors); + CoppOrch(swss::DBConnector* db, std::string tableName); protected: object_map m_trap_group_map; - bool enable_sflow_trap; TrapGroupPolicerTable m_trap_group_policer_map; TrapIdTrapObjectsTable m_syncdTrapIds; TrapGroupHostIfMap m_trap_group_hostif_map; TrapIdHostIfTableMap m_trapid_hostif_table_map; + TrapGroupTrapIdAttribs m_trap_group_trap_id_attrs; void initDefaultHostIntfTable(); void initDefaultTrapGroup(); @@ -64,7 +67,6 @@ class CoppOrch : public Orch task_process_status processCoppRule(Consumer& consumer); bool isValidList(std::vector &trap_id_list, std::vector &all_items) const; void getTrapIdList(std::vector &trap_id_name_list, std::vector &trap_id_list) const; - bool applyTrapIds(sai_object_id_t trap_group, std::vector &trap_id_name_list, std::vector &trap_id_attribs); bool applyAttributesToTrapIds(sai_object_id_t trap_group_id, const std::vector &trap_id_list, std::vector &trap_id_attribs); bool createPolicer(std::string trap_group, std::vector &policer_attribs); @@ -74,8 +76,28 @@ class CoppOrch : public Orch bool createGenetlinkHostIf(std::string trap_group_name, std::vector &hostif_attribs); bool removeGenetlinkHostIf(std::string trap_group_name); - bool createGenetlinkHostIfTable(std::vector &trap_id_name_list); - void coppProcessSflow(Consumer& consumer); + bool createGenetlinkHostIfTable(std::vector &trap_id_list); + bool removeGenetlinkHostIfTable(std::vector &trap_id_list); + void getTrapAddandRemoveList(std::string trap_group_name, std::vector &trap_ids, + std::vector &add_trap_ids, + std::vector &rem_trap_ids); + + void getTrapIdsFromTrapGroup (sai_object_id_t trap_group_obj, + std::vector &trap_ids); + + bool trapGroupProcessTrapIdChange (std::string trap_group_name, + std::vector &add_trap_ids, + std::vector &rem_trap_ids); + + bool processTrapGroupDel (std::string trap_group_name); + + bool getAttribsFromTrapGroup (std::vector &fv_tuple, + std::vector &trap_gr_attribs, + std::vector &trap_id_attribs, + std::vector &policer_attribs, + std::vector &genetlink_attribs); + + bool trapGroupUpdatePolicer (std::string trap_group_name, std::vector &policer_attribs); virtual void doTask(Consumer& consumer); }; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index eec6a6bd0bd3..89f28fb101ce 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -132,14 +132,7 @@ bool OrchDaemon::init() gDirectory.set(gFgNhgOrch); gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gSwitchOrch, gNeighOrch, gIntfsOrch, vrf_orch, gFgNhgOrch); - TableConnector confDbSflowTable(m_configDb, CFG_SFLOW_TABLE_NAME); - TableConnector appCoppTable(m_applDb, APP_COPP_TABLE_NAME); - - vector copp_table_connectors = { - confDbSflowTable, - appCoppTable - }; - CoppOrch *copp_orch = new CoppOrch(copp_table_connectors); + CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); diff --git a/tests/conftest.py b/tests/conftest.py index 43bf40e24d86..469f5767830a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,7 +90,7 @@ def _verify_db_contents(): if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VLAN")) != 1: return (False, None) - if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF")) != NUM_PORTS: + if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF")) < NUM_PORTS: return (False, None) if len(self.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY")) != 0: diff --git a/tests/test_copp.py b/tests/test_copp.py new file mode 100644 index 000000000000..59cf8b4c3fbf --- /dev/null +++ b/tests/test_copp.py @@ -0,0 +1,752 @@ +import time +import os +import pytest + +from swsscommon import swsscommon + + +field_to_sai_attr = { + "queue": "SAI_HOSTIF_TRAP_GROUP_ATTR_QUEUE", + "meter_type": "SAI_POLICER_ATTR_METER_TYPE", + "mode": "SAI_POLICER_ATTR_MODE", + "trap_action": "SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION", + "trap_priority": "SAI_HOSTIF_TRAP_ATTR_TRAP_PRIORITY", + "cbs": "SAI_POLICER_ATTR_CBS", + "cir": "SAI_POLICER_ATTR_CIR", + "red_action": "SAI_POLICER_ATTR_RED_PACKET_ACTION" +} + +field_to_sai_obj_type = { + "queue": "SAI_OBJECT_TYPE_HOSTIF_TRAP_GROUP", + "meter_type": "SAI_OBJECT_TYPE_POLICER", + "mode": "SAI_OBJECT_TYPE_POLICER", + "trap_action": "SAI_OBJECT_TYPE_HOSTIF_TRAP", + "trap_priority": "SAI_OBJECT_TYPE_HOSTIF_TRAP", + "cbs": "SAI_OBJECT_TYPE_POLICER", + "cir": "SAI_OBJECT_TYPE_POLICER", + "red_action": "SAI_OBJECT_TYPE_POLICER", + "genetlink_mcgrp_name": "SAI_OBJECT_TYPE_HOSTIF", + "genetlink_name": "SAI_OBJECT_TYPE_HOSTIF" +} + +traps_to_trap_type = { + "stp": "SAI_HOSTIF_TRAP_TYPE_STP", + "lacp": "SAI_HOSTIF_TRAP_TYPE_LACP", + "eapol": "SAI_HOSTIF_TRAP_TYPE_EAPOL", + "lldp": "SAI_HOSTIF_TRAP_TYPE_LLDP", + "pvrst": "SAI_HOSTIF_TRAP_TYPE_PVRST", + "igmp_query": "SAI_HOSTIF_TRAP_TYPE_IGMP_TYPE_QUERY", + "igmp_leave": "SAI_HOSTIF_TRAP_TYPE_IGMP_TYPE_LEAVE", + "igmp_v1_report": "SAI_HOSTIF_TRAP_TYPE_IGMP_TYPE_V1_REPORT", + "igmp_v2_report": "SAI_HOSTIF_TRAP_TYPE_IGMP_TYPE_V2_REPORT", + "igmp_v3_report": "SAI_HOSTIF_TRAP_TYPE_IGMP_TYPE_V3_REPORT", + "sample_packet": "SAI_HOSTIF_TRAP_TYPE_SAMPLEPACKET", + "switch_cust_range": "SAI_HOSTIF_TRAP_TYPE_SWITCH_CUSTOM_RANGE_BASE", + "arp_req": "SAI_HOSTIF_TRAP_TYPE_ARP_REQUEST", + "arp_resp": "SAI_HOSTIF_TRAP_TYPE_ARP_RESPONSE", + "dhcp": "SAI_HOSTIF_TRAP_TYPE_DHCP", + "ospf": "SAI_HOSTIF_TRAP_TYPE_OSPF", + "pim": "SAI_HOSTIF_TRAP_TYPE_PIM", + "vrrp": "SAI_HOSTIF_TRAP_TYPE_VRRP", + "bgp": "SAI_HOSTIF_TRAP_TYPE_BGP", + "dhcpv6": "SAI_HOSTIF_TRAP_TYPE_DHCPV6", + "ospfv6": "SAI_HOSTIF_TRAP_TYPE_OSPFV6", + "vrrpv6": "SAI_HOSTIF_TRAP_TYPE_VRRPV6", + "bgpv6": "SAI_HOSTIF_TRAP_TYPE_BGPV6", + "neigh_discovery": "SAI_HOSTIF_TRAP_TYPE_IPV6_NEIGHBOR_DISCOVERY", + "mld_v1_v2": "SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_V2", + "mld_v1_report": "SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_REPORT", + "mld_v1_done": "SAI_HOSTIF_TRAP_TYPE_IPV6_MLD_V1_DONE", + "mld_v2_report": "SAI_HOSTIF_TRAP_TYPE_MLD_V2_REPORT", + "ip2me": "SAI_HOSTIF_TRAP_TYPE_IP2ME", + "ssh": "SAI_HOSTIF_TRAP_TYPE_SSH", + "snmp": "SAI_HOSTIF_TRAP_TYPE_SNMP", + "router_custom_range": "SAI_HOSTIF_TRAP_TYPE_ROUTER_CUSTOM_RANGE_BASE", + "l3_mtu_error": "SAI_HOSTIF_TRAP_TYPE_L3_MTU_ERROR", + "ttl_error": "SAI_HOSTIF_TRAP_TYPE_TTL_ERROR", + "udld": "SAI_HOSTIF_TRAP_TYPE_UDLD", + "bfd": "SAI_HOSTIF_TRAP_TYPE_BFD", + "bfdv6": "SAI_HOSTIF_TRAP_TYPE_BFDV6", + "src_nat_miss": "SAI_HOSTIF_TRAP_TYPE_SNAT_MISS", + "dest_nat_miss": "SAI_HOSTIF_TRAP_TYPE_DNAT_MISS" + } + +copp_group_default = { + "queue": "0", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"600", + "cbs":"600", + "red_action":"drop" +} + +copp_group_queue4_group1 = { + "trap_action":"trap", + "trap_priority":"4", + "queue": "4" +} + +copp_group_queue4_group2 = { + "trap_action":"copy", + "trap_priority":"4", + "queue": "4", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"600", + "cbs":"600", + "red_action":"drop" +} + +copp_group_queue4_group3 = { + "trap_action":"trap", + "trap_priority":"4", + "queue": "4" +} + +copp_group_queue1_group1 = { + "trap_action":"trap", + "trap_priority":"1", + "queue": "1", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"6000", + "cbs":"6000", + "red_action":"drop" +} + +copp_group_queue1_group2 = { + "trap_action":"trap", + "trap_priority":"1", + "queue": "1", + "meter_type":"packets", + "mode":"sr_tcm", + "cir":"600", + "cbs":"600", + "red_action":"drop" +} + +copp_group_queue2_group1 = { + "cbs": "1000", + "cir": "1000", + "genetlink_mcgrp_name": "packets", + "genetlink_name": "psample", + "meter_type": "packets", + "mode": "sr_tcm", + "queue": "2", + "red_action": "drop", + "trap_action": "trap", + "trap_priority": "1" +} + +copp_group_queue5_group1 = { + "cbs": "2000", + "cir": "2000", + "meter_type": "packets", + "mode": "sr_tcm", + "queue": "5", + "red_action": "drop", + "trap_action": "trap", + "trap_priority": "5" +} +copp_trap = { + "bgp,bgpv6": copp_group_queue4_group1, + "lacp": copp_group_queue4_group1, + "arp_req,arp_resp,neigh_discovery":copp_group_queue4_group2, + "lldp":copp_group_queue4_group3, + "dhcp,dhcpv6":copp_group_queue4_group3, + "udld":copp_group_queue4_group3, + "ip2me":copp_group_queue1_group1, + "src_nat_miss,dest_nat_miss": copp_group_queue1_group2, + "sample_packet": copp_group_queue2_group1, + "ttl_error": copp_group_default +} + +disabled_traps = ["sample_packet"] + +policer_meter_map = { + "packets": "SAI_METER_TYPE_PACKETS", + "bytes": "SAI_METER_TYPE_BYTES" +} + +policer_mode_map = { + "sr_tcm": "SAI_POLICER_MODE_SR_TCM", + "tr_tcm": "SAI_POLICER_MODE_TR_TCM", + "storm": "SAI_POLICER_MODE_STORM_CONTROL" +} + + +packet_action_map = { + "drop": "SAI_PACKET_ACTION_DROP", + "forward": "SAI_PACKET_ACTION_FORWARD", + "copy": "SAI_PACKET_ACTION_COPY", + "copy_cancel": "SAI_PACKET_ACTION_COPY_CANCEL", + "trap": "SAI_PACKET_ACTION_TRAP", + "log": "SAI_PACKET_ACTION_LOG", + "deny": "SAI_PACKET_ACTION_DENY", + "transit": "SAI_PACKET_ACTION_TRANSIT" +} + +class TestCopp(object): + def setup_copp(self, dvs): + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.trap_atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF_TRAP") + self.trap_group_atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF_TRAP_GROUP") + self.policer_atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + self.hostiftbl_atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF_TABLE_ENTRY") + self.hostif_atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF") + self.trap_ctbl = swsscommon.Table(self.cdb, "COPP_TRAP") + self.trap_group_ctbl = swsscommon.Table(self.cdb, "COPP_GROUP") + self.feature_tbl = swsscommon.Table(self.cdb, "FEATURE") + fvs = swsscommon.FieldValuePairs([("state", "disbled")]) + self.feature_tbl.set("sflow", fvs) + time.sleep(2) + + + def validate_policer(self, policer_oid, field, value): + (status, fvs) = self.policer_atbl.get(policer_oid) + assert status == True + attr = field_to_sai_attr[field] + + attr_value = value + if field == "mode": + attr_value = policer_mode_map[value] + elif field == "meter_type": + attr_value = policer_meter_map[value] + elif field == "red_action": + attr_value = packet_action_map[value] + + for fv in fvs: + if (fv[0] == attr): + assert attr_value == fv[1] + + def validate_trap_group(self, trap_oid, trap_group): + (status, trap_fvs) = self.trap_atbl.get(trap_oid) + assert status == True + trap_group_oid = "" + policer_oid= "" + queue = "" + trap_action = "" + trap_priority = "" + + for fv in trap_fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_PACKET_ACTION": + trap_action = fv[1] + elif fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_PRIORITY": + trap_priority = fv[1] + elif fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_GROUP": + trap_group_oid = fv[1] + + if trap_group_oid != "" and trap_group_oid != "oid:0x0": + (status, fvs) = self.trap_group_atbl.get(trap_group_oid) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_GROUP_ATTR_POLICER": + policer_oid = fv[1] + elif fv[0] == "SAI_HOSTIF_TRAP_GROUP_ATTR_QUEUE": + queue = fv[1] + + for keys in trap_group: + obj_type = field_to_sai_obj_type[keys] + if obj_type == "SAI_OBJECT_TYPE_POLICER": + assert policer_oid != "" + assert policer_oid != "oid:0x0" + self.validate_policer(policer_oid, keys, trap_group[keys]) + + elif obj_type == "SAI_OBJECT_TYPE_HOSTIF_TRAP_GROUP": + assert trap_group_oid != "" + assert trap_group_oid != "oid:0x0" + if keys == "queue": + assert queue == trap_group[keys] + else: + assert 0 + + elif obj_type == "SAI_OBJECT_TYPE_HOSTIF_TRAP": + if keys == "trap_action": + assert trap_action == packet_action_map[trap_group[keys]] + elif keys == "trap_priority": + assert trap_priority == trap_group[keys] + + elif obj_type == "SAI_OBJECT_TYPE_HOSTIF": + host_tbl_keys = self.hostiftbl_atbl.getKeys(); + host_tbl_key = None + for host_tbl_entry in host_tbl_keys: + (status, fvs) = self.hostiftbl_atbl.get(host_tbl_entry) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TABLE_ENTRY_ATTR_TRAP_ID": + if fv[1] == trap_oid: + host_tbl_key = host_tbl_entry + break + if host_tbl_key != None: + break + assert host_tbl_key != None + (status, fvs) = self.hostiftbl_atbl.get(host_tbl_key) + hostif = None + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TABLE_ENTRY_ATTR_HOST_IF": + hostif = fv[1] + elif fv[0] == "SAI_HOSTIF_TABLE_ENTRY_ATTR_CHANNEL_TYPE": + assert fv[1] == "SAI_HOSTIF_TABLE_ENTRY_CHANNEL_TYPE_GENETLINK" + assert hostif != None + (status, fvs) = self.hostif_atbl.get(hostif) + assert status == True + for fv in fvs: + if keys == "genetlink_mcgrp_name": + if fv[0] == "SAI_HOSTIF_ATTR_GENETLINK_MCGRP_NAME": + assert fv[1] == trap_group[keys] + if keys == "genetlink_name": + if fv[0] == "SAI_HOSTIF_ATTR_NAME": + assert fv[1] == trap_group[keys] + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_defaults(self, dvs, testlog): + self.setup_copp(dvs) + trap_keys = self.trap_atbl.getKeys() + for traps in copp_trap: + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_restricted_trap_sflow(self, dvs, testlog): + self.setup_copp(dvs) + fvs = swsscommon.FieldValuePairs([("state", "enabled")]) + self.feature_tbl.set("sflow", fvs) + time.sleep(2) + global copp_trap + + trap_keys = self.trap_atbl.getKeys() + for traps in copp_trap: + trap_ids = traps.split(",") + if "sample_packet" not in trap_ids: + continue + trap_group = copp_trap[traps] + trap_found = False + trap_type = traps_to_trap_type["sample_packet"] + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + assert trap_found == True + + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_policer_set(self, dvs, testlog): + self.setup_copp(dvs) + fvs = swsscommon.FieldValuePairs([("cbs", "900")]) + self.trap_group_ctbl.set("queue4_group2", fvs) + copp_group_queue4_group2["cbs"] = "900" + time.sleep(2) + global copp_trap + + trap_keys = self.trap_atbl.getKeys() + for traps in copp_trap: + if copp_trap[traps] != copp_group_queue4_group2: + continue + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_trap_group_set(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "bgp,bgpv6" + fvs = swsscommon.FieldValuePairs([("trap_group", "queue1_group1")]) + self.trap_ctbl.set("bgp", fvs) + copp_trap[traps] = copp_group_queue1_group1 + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_trap_ids_set(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "bgp" + fvs = swsscommon.FieldValuePairs([("trap_ids", traps)]) + self.trap_ctbl.set("bgp", fvs) + time.sleep(2) + + old_traps = "bgp,bgpv6" + trap_keys = self.trap_atbl.getKeys() + trap_ids = old_traps.split(",") + trap_group = copp_trap[old_traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id == "bgp": + assert trap_found == True + elif trap_id == "bgpv6": + assert trap_found == False + + traps = "bgp,bgpv6" + fvs = swsscommon.FieldValuePairs([("trap_ids", traps)]) + self.trap_ctbl.set("bgp", fvs) + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_trap_action_set(self, dvs, testlog): + self.setup_copp(dvs) + fvs = swsscommon.FieldValuePairs([("trap_action", "copy")]) + self.trap_group_ctbl.set("queue4_group1", fvs) + copp_group_queue4_group1["trap_action"] = "copy" + time.sleep(2) + global copp_trap + + trap_keys = self.trap_atbl.getKeys() + for traps in copp_trap: + if copp_trap[traps] != copp_group_queue4_group1: + continue + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_new_trap_add(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "eapol" + fvs = swsscommon.FieldValuePairs([("trap_group", "queue1_group2"),("trap_ids", "eapol")]) + self.trap_ctbl.set(traps, fvs) + copp_trap[traps] = copp_group_queue1_group2 + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_new_trap_del(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "eapol" + fvs = swsscommon.FieldValuePairs([("trap_group", "queue1_group2"),("trap_ids", "eapol")]) + self.trap_ctbl.set(traps, fvs) + copp_trap[traps] = copp_group_queue1_group2 + time.sleep(2) + + self.trap_ctbl._del(traps) + time.sleep(2) + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + trap_keys = self.trap_atbl.getKeys() + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == False + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_new_trap_group_add(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "igmp_v1_report" + list_val = list(copp_group_queue5_group1.items()) + + fvs = swsscommon.FieldValuePairs(list_val) + self.trap_group_ctbl.set("queue5_group1", fvs) + traps = "igmp_v1_report" + t_fvs = swsscommon.FieldValuePairs([("trap_group", "queue5_group1"),("trap_ids", "igmp_v1_report")]) + self.trap_ctbl.set(traps, t_fvs) + copp_trap[traps] = copp_group_queue5_group1 + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_new_trap_group_del(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "igmp_v1_report" + list_val = list(copp_group_queue5_group1.items()) + + fvs = swsscommon.FieldValuePairs(list_val) + self.trap_group_ctbl.set("queue5_group1", fvs) + traps = "igmp_v1_report" + t_fvs = swsscommon.FieldValuePairs([("trap_group", "queue5_group1"),("trap_ids", "igmp_v1_report")]) + self.trap_ctbl.set(traps, t_fvs) + copp_trap[traps] = copp_group_queue5_group1 + + self.trap_group_ctbl._del("queue5_group1") + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found != True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_override_trap_grp_cfg_del (self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + fvs = swsscommon.FieldValuePairs([("cbs", "500"),("cir","500")]) + self.trap_group_ctbl.set("queue1_group1", fvs) + time.sleep(2) + + + self.trap_group_ctbl._del("queue1_group1") + time.sleep(2) + + + trap_keys = self.trap_atbl.getKeys() + for traps in copp_trap: + if copp_trap[traps] != copp_group_queue1_group1: + continue + trap_ids = traps.split(",") + trap_group = copp_trap[traps] + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + assert trap_found == True + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_override_trap_cfg_del(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + traps = "ip2me,ssh" + fvs = swsscommon.FieldValuePairs([("trap_ids", "ip2me,ssh")]) + self.trap_ctbl.set("ip2me", fvs) + time.sleep(2) + + self.trap_ctbl._del("ip2me") + time.sleep(2) + trap_ids = traps.split(",") + trap_group = copp_trap["ip2me"] + trap_keys = self.trap_atbl.getKeys() + for trap_id in trap_ids: + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + if trap_id not in disabled_traps: + if trap_id == "ip2me": + assert trap_found == True + elif trap_id == "ssh": + assert trap_found == False + + @pytest.mark.skip("Skip to be removed after sonic-buildimage changes get merged") + def test_empty_trap_cfg(self, dvs, testlog): + self.setup_copp(dvs) + global copp_trap + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + self.trap_ctbl.set("ip2me", fvs) + time.sleep(2) + + trap_id = "ip2me" + trap_group = copp_trap["ip2me"] + trap_keys = self.trap_atbl.getKeys() + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + assert trap_found == False + + self.trap_ctbl._del("ip2me") + time.sleep(2) + + trap_keys = self.trap_atbl.getKeys() + trap_type = traps_to_trap_type[trap_id] + trap_found = False + trap_group_oid = "" + for key in trap_keys: + (status, fvs) = self.trap_atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_HOSTIF_TRAP_ATTR_TRAP_TYPE": + if fv[1] == trap_type: + trap_found = True + if trap_found: + self.validate_trap_group(key,trap_group) + break + assert trap_found == True