diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 083fb1047c..e3255ba15b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -92,6 +92,45 @@ stages: artifact_name: sonic-swss.arm64 archive_gcov: false +- stage: BuildBookworm + dependsOn: BuildArm + condition: succeeded('BuildArm') + jobs: + - template: .azure-pipelines/build-template.yml + parameters: + arch: amd64 + pool: sonicbld + sonic_slave: sonic-slave-bookworm + common_lib_artifact_name: common-lib + swss_common_artifact_name: sonic-swss-common-bookworm + sairedis_artifact_name: sonic-sairedis-bookworm + artifact_name: sonic-swss-bookworm + archive_gcov: false + + - template: .azure-pipelines/build-template.yml + parameters: + arch: armhf + timeout: 240 + pool: sonicbld-armhf + sonic_slave: sonic-slave-bookworm-armhf + common_lib_artifact_name: common-lib.armhf + swss_common_artifact_name: sonic-swss-common-bookworm.armhf + sairedis_artifact_name: sonic-sairedis-bookworm.armhf + artifact_name: sonic-swss-bookworm.armhf + archive_gcov: false + + - template: .azure-pipelines/build-template.yml + parameters: + arch: arm64 + timeout: 240 + pool: sonicbld-arm64 + sonic_slave: sonic-slave-bookworm-arm64 + common_lib_artifact_name: common-lib.arm64 + swss_common_artifact_name: sonic-swss-common-bookworm.arm64 + sairedis_artifact_name: sonic-sairedis-bookworm.arm64 + artifact_name: sonic-swss-bookworm.arm64 + archive_gcov: false + - stage: BuildDocker dependsOn: Build condition: succeeded('Build') diff --git a/cfgmgr/buffer_pool_mellanox.lua b/cfgmgr/buffer_pool_mellanox.lua index ee48fe0403..15bb81efb2 100644 --- a/cfgmgr/buffer_pool_mellanox.lua +++ b/cfgmgr/buffer_pool_mellanox.lua @@ -133,7 +133,7 @@ local function iterate_profile_list(all_items) return 0 end -local function fetch_buffer_pool_size_from_appldb() +local function fetch_buffer_pool_size_from_appldb(shp_enabled) local buffer_pools = {} redis.call('SELECT', config_db) local buffer_pool_keys = redis.call('KEYS', 'BUFFER_POOL|*') @@ -158,7 +158,18 @@ local function fetch_buffer_pool_size_from_appldb() end xoff = redis.call('HGET', 'BUFFER_POOL_TABLE:' .. buffer_pools[i], 'xoff') if not xoff then - table.insert(result, buffer_pools[i] .. ':' .. size) + if shp_enabled and size == "0" and buffer_pools[i] == "ingress_lossless_pool" then + -- During initialization, if SHP is enabled + -- 1. the buffer pool sizes, xoff have initialized to 0, which means the shared headroom pool is disabled + -- 2. but the buffer profiles already indicate the shared headroom pool is enabled + -- 3. later on the buffer pool sizes are updated with xoff being non-zero + -- In case the orchagent starts handling buffer configuration between 2 and 3, + -- It is inconsistent between buffer pools and profiles, which fails Mellanox SAI sanity check + -- To avoid it, it indicates the shared headroom pool is enabled by setting a very small buffer pool and shared headroom pool sizes + table.insert(result, buffer_pools[i] .. ':2048:1024') + else + table.insert(result, buffer_pools[i] .. ':' .. size) + end else table.insert(result, buffer_pools[i] .. ':' .. size .. ':' .. xoff) end @@ -295,7 +306,7 @@ local fail_count = 0 fail_count = fail_count + iterate_all_items(all_pgs, true) fail_count = fail_count + iterate_all_items(all_tcs, false) if fail_count > 0 then - fetch_buffer_pool_size_from_appldb() + fetch_buffer_pool_size_from_appldb(shp_enabled) return result end @@ -305,7 +316,7 @@ local all_egress_profile_lists = redis.call('KEYS', 'BUFFER_PORT_EGRESS_PROFILE_ fail_count = fail_count + iterate_profile_list(all_ingress_profile_lists) fail_count = fail_count + iterate_profile_list(all_egress_profile_lists) if fail_count > 0 then - fetch_buffer_pool_size_from_appldb() + fetch_buffer_pool_size_from_appldb(shp_enabled) return result end diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index ba247197c1..32f71d8280 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -549,24 +549,23 @@ void BufferMgr::doTask(Consumer &consumer) task_status = doSpeedUpdateTask(port); } } - - switch (task_status) - { - case task_process_status::task_failed: - SWSS_LOG_ERROR("Failed to process table update"); - return; - case task_process_status::task_need_retry: - SWSS_LOG_INFO("Unable to process table update. Will retry..."); - ++it; - break; - case task_process_status::task_invalid_entry: - SWSS_LOG_ERROR("Failed to process invalid entry, drop it"); - it = consumer.m_toSync.erase(it); - break; - default: - it = consumer.m_toSync.erase(it); - break; - } + } + switch (task_status) + { + case task_process_status::task_failed: + SWSS_LOG_ERROR("Failed to process table update"); + return; + case task_process_status::task_need_retry: + SWSS_LOG_INFO("Unable to process table update. Will retry..."); + ++it; + break; + case task_process_status::task_invalid_entry: + SWSS_LOG_ERROR("Failed to process invalid entry, drop it"); + it = consumer.m_toSync.erase(it); + break; + default: + it = consumer.m_toSync.erase(it); + break; } } } diff --git a/cfgmgr/coppmgr.cpp b/cfgmgr/coppmgr.cpp index cfa94988d9..9b2c3ee4d7 100644 --- a/cfgmgr/coppmgr.cpp +++ b/cfgmgr/coppmgr.cpp @@ -21,10 +21,11 @@ static set g_copp_init_set; void CoppMgr::parseInitFile(void) { - std::ifstream ifs(COPP_INIT_FILE); + std::ifstream ifs(m_coppCfgfile); + if (ifs.fail()) { - SWSS_LOG_ERROR("COPP init file %s not found", COPP_INIT_FILE); + SWSS_LOG_ERROR("COPP init file %s not found", m_coppCfgfile.c_str()); return; } json j = json::parse(ifs); @@ -293,7 +294,7 @@ bool CoppMgr::isDupEntry(const std::string &key, std::vector &f return true; } -CoppMgr::CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : +CoppMgr::CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames, const string copp_init_file) : Orch(cfgDb, tableNames), m_cfgCoppTrapTable(cfgDb, CFG_COPP_TRAP_TABLE_NAME), m_cfgCoppGroupTable(cfgDb, CFG_COPP_GROUP_TABLE_NAME), @@ -301,7 +302,8 @@ CoppMgr::CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c 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) + m_coppTable(appDb, APP_COPP_TABLE_NAME), + m_coppCfgfile(copp_init_file) { SWSS_LOG_ENTER(); parseInitFile(); diff --git a/cfgmgr/coppmgr.h b/cfgmgr/coppmgr.h index 44549d3bec..86f1b0e4e2 100644 --- a/cfgmgr/coppmgr.h +++ b/cfgmgr/coppmgr.h @@ -62,7 +62,7 @@ class CoppMgr : public Orch { public: CoppMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, - const std::vector &tableNames); + const std::vector &tableNames, const std::string copp_init_file = COPP_INIT_FILE); using Orch::doTask; private: @@ -75,6 +75,7 @@ class CoppMgr : public Orch CoppCfg m_coppGroupInitCfg; CoppCfg m_coppTrapInitCfg; CoppCfg m_featuresCfgTable; + std::string m_coppCfgfile; void doTask(Consumer &consumer); diff --git a/cfgmgr/fabricmgr.cpp b/cfgmgr/fabricmgr.cpp index bcbaa5726a..16a8111199 100644 --- a/cfgmgr/fabricmgr.cpp +++ b/cfgmgr/fabricmgr.cpp @@ -105,12 +105,12 @@ bool FabricMgr::writeConfigToAppDb(const std::string &key, const std::string &fi if (key == "FABRIC_MONITOR_DATA") { m_appFabricMonitorTable.set(key, fvs); - SWSS_LOG_NOTICE("Write FABRIC_MONITOR:%s %s to %s", key.c_str(), field.c_str(), value.c_str()); + SWSS_LOG_INFO("Write FABRIC_MONITOR:%s %s to %s", key.c_str(), field.c_str(), value.c_str()); } else { m_appFabricPortTable.set(key, fvs); - SWSS_LOG_NOTICE("Write FABRIC_PORT:%s %s to %s", key.c_str(), field.c_str(), value.c_str()); + SWSS_LOG_INFO("Write FABRIC_PORT:%s %s to %s", key.c_str(), field.c_str(), value.c_str()); } return true; diff --git a/cfgmgr/fabricmgr.h b/cfgmgr/fabricmgr.h index dbe2fd0d89..1fd399fef9 100644 --- a/cfgmgr/fabricmgr.h +++ b/cfgmgr/fabricmgr.h @@ -21,7 +21,7 @@ class FabricMgr : public Orch Table m_cfgFabricMonitorTable; Table m_cfgFabricPortTable; Table m_appFabricMonitorTable; - Table m_appFabricPortTable; + ProducerStateTable m_appFabricPortTable; void doTask(Consumer &consumer); bool writeConfigToAppDb(const std::string &alias, const std::string &field, const std::string &value); diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 78c9030807..99a1083ad6 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -28,19 +28,24 @@ using namespace swss; #define LOOPBACK_DEFAULT_MTU_STR "65536" #define DEFAULT_MTU_STR 9100 +extern MacAddress gMacAddress; + IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME), m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME), m_cfgLagIntfTable(cfgDb, CFG_LAG_INTF_TABLE_NAME), m_cfgLoopbackIntfTable(cfgDb, CFG_LOOPBACK_INTERFACE_TABLE_NAME), + m_cfgSagTable(cfgDb, CFG_SAG_TABLE_NAME), m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME), m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME), m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME), - m_neighTable(appDb, APP_NEIGH_TABLE_NAME) + m_appSagTableProducer(appDb, APP_SAG_TABLE_NAME), + m_neighTable(appDb, APP_NEIGH_TABLE_NAME), + m_appLagTable(appDb, APP_LAG_TABLE_NAME) { auto subscriberStateTable = new swss::SubscriberStateTable(stateDb, STATE_PORT_TABLE_NAME, TableConsumable::DEFAULT_POP_BATCH_SIZE, 100); @@ -193,6 +198,27 @@ bool IntfMgr::setIntfMpls(const string &alias, const string& mpls) return true; } +void IntfMgr::setIntfState(const string &alias, bool isUp) +{ + stringstream cmd; + string res; + + if (isUp) + { + cmd << IP_CMD << " link set " << shellquote(alias) << " up"; + } + else + { + cmd << IP_CMD << " link set " << shellquote(alias) << " down"; + } + + int ret = swss::exec(cmd.str(), res); + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } +} + void IntfMgr::addLoopbackIntf(const string &alias) { stringstream cmd; @@ -765,6 +791,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string grat_arp = ""; string mpls = ""; string ipv6_link_local_mode = ""; + string sag = ""; string loopback_action = ""; for (auto idx : data) @@ -804,6 +831,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { ipv6_link_local_mode = value; } + else if (field == "static_anycast_gateway") + { + sag = value; + } else if (field == "vlan") { vlanId = value; @@ -979,8 +1010,43 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, } else { - FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); - data.push_back(fvTuple); + if (!sag.empty()) + { + // only VLAN interface can set static anycast gateway + if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + string gwmac = ""; + if (m_cfgSagTable.hget("GLOBAL", "gateway_mac", gwmac)) + { + // before change interface MAC, set interface down and up to regenerate IPv6 LL by MAC + if (sag == "true") + { + setIntfState(alias, false); + setIntfMac(alias, gwmac); + setIntfState(alias, true); + + FieldValueTuple fvTuple("mac_addr", gwmac); + data.push_back(fvTuple); + } + else if (sag == "false") + { + setIntfState(alias, false); + setIntfMac(alias, gMacAddress.to_string()); + setIntfState(alias, true); + + FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); + data.push_back(fvTuple); + } else { + SWSS_LOG_ERROR("invalid SAG config \"%s\", it should be \"true\" or \"false\"", sag.c_str()); + } + } + } + } + else + { + FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); + data.push_back(fvTuple); + } } if (!proxy_arp.empty()) @@ -1115,6 +1181,42 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, return true; } +void IntfMgr::doSagTask(const vector& keys, + const vector &data, + const string& op) +{ + SWSS_LOG_ENTER(); + + string mac = ""; + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + + if (field == "gateway_mac") + { + mac = value; + } + } + + vector fvAppSag; + if (op == SET_COMMAND) + { + FieldValueTuple gwmac("gateway_mac", MacAddress(mac).to_string()); + fvAppSag.push_back(gwmac); + m_appSagTableProducer.set("GLOBAL", fvAppSag); + + updateSagMac(mac); + } + else if (op == DEL_COMMAND) + { + m_appSagTableProducer.del("GLOBAL"); + + // reset mac address for enabled static-anycast-gateway's VLAN interfaces + updateSagMac(gMacAddress.to_string()); + } +} + void IntfMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -1147,6 +1249,14 @@ void IntfMgr::doTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } + + if (table_name == CFG_SAG_TABLE_NAME) + { + doSagTask(keys, data, op); + it = consumer.m_toSync.erase(it); + continue; + } + if (!doIntfGeneralTask(keys, data, op)) { it++; @@ -1209,6 +1319,56 @@ void IntfMgr::doPortTableTask(const string& key, vector data, s } } +void IntfMgr::updateSagMac(const std::string &macAddr) +{ + vector keys; + m_cfgVlanIntfTable.getKeys(keys); + for (auto &key: keys) + { + vector entryKeys = tokenize(key, config_db_key_delimiter); + if (key.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + continue; + } + + // only process the entry includes the SAG's config + // e.g. VLAN_INTERFACE|Vlan201 + if (entryKeys.size() != 1) + { + continue; + } + + string value = ""; + if (m_cfgVlanIntfTable.hget(key, "static_anycast_gateway", value)) + { + if (value == "true") + { + SWSS_LOG_NOTICE("set %s mac address to %s", key.c_str(), macAddr.c_str()); + + // enable SAG, set device down and up to regenerate IPv6 LL by MAC + setIntfState(key, false); + setIntfMac(key, macAddr); + setIntfState(key, true); + + vector vlanIntFv; + + // keep consistent with default MAC 00:00:00:00:00:00 + string entryMac = MacAddress().to_string(); + if (macAddr != gMacAddress.to_string()) + { + entryMac = macAddr; + } + + FieldValueTuple fvTuple("mac_addr", entryMac); + vlanIntFv.push_back(fvTuple); + m_appIntfTableProducer.set(key, vlanIntFv); + } + } else { + SWSS_LOG_INFO("can't get %s in VLAN_INTERFACE table", key.c_str()); + } + } +} + bool IntfMgr::enableIpv6Flag(const string &alias) { stringstream cmd; diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 4eca2402ce..21ff856b08 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -28,9 +28,9 @@ class IntfMgr : public Orch using Orch::doTask; private: - ProducerStateTable m_appIntfTableProducer; - Table m_cfgIntfTable, m_cfgVlanIntfTable, m_cfgLagIntfTable, m_cfgLoopbackIntfTable; - Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable; + ProducerStateTable m_appIntfTableProducer, m_appSagTableProducer; + Table m_cfgIntfTable, m_cfgVlanIntfTable, m_cfgLagIntfTable, m_cfgLoopbackIntfTable, m_cfgSagTable; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable, m_appLagTable; Table m_neighTable; SubIntfMap m_subIntfList; @@ -43,9 +43,11 @@ class IntfMgr : public Orch void setIntfVrf(const std::string &alias, const std::string &vrfName); void setIntfMac(const std::string &alias, const std::string &macAddr); bool setIntfMpls(const std::string &alias, const std::string &mpls); + void setIntfState(const std::string &alias, bool isUp); bool doIntfGeneralTask(const std::vector& keys, std::vector data, const std::string& op); bool doIntfAddrTask(const std::vector& keys, const std::vector& data, const std::string& op); + void doSagTask(const std::vector& keys, const std::vector& data, const std::string& op); void doTask(Consumer &consumer); void doPortTableTask(const std::string& key, std::vector data, std::string op); @@ -75,6 +77,7 @@ class IntfMgr : public Orch void updateSubIntfAdminStatus(const std::string &alias, const std::string &admin); void updateSubIntfMtu(const std::string &alias, const std::string &mtu); + void updateSagMac(const std::string &macAddr); bool enableIpv6Flag(const std::string&); bool m_replayDone {false}; diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index e414590920..50ca4467ef 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "dbconnector.h" #include "select.h" #include "exec.h" @@ -16,6 +17,8 @@ using namespace swss; /* select() function timeout retry time, in millisecond */ #define SELECT_TIMEOUT 1000 +MacAddress gMacAddress; + int main(int argc, char **argv) { Logger::linkToDbNative("intfmgrd"); @@ -32,6 +35,7 @@ int main(int argc, char **argv) CFG_LOOPBACK_INTERFACE_TABLE_NAME, CFG_VLAN_SUB_INTF_TABLE_NAME, CFG_VOQ_INBAND_INTERFACE_TABLE_NAME, + CFG_SAG_TABLE_NAME, }; DBConnector cfgDb("CONFIG_DB", 0); @@ -50,6 +54,15 @@ int main(int argc, char **argv) s.addSelectables(o->getSelectables()); } + Table table(&cfgDb, "DEVICE_METADATA"); + string mac = ""; + if (!table.hget("localhost", "mac", mac)) + { + throw runtime_error("couldn't find MAC address of the device from config DB"); + } + + gMacAddress = MacAddress(mac); + SWSS_LOG_NOTICE("starting main loop"); while (true) { diff --git a/configure.ac b/configure.ac index 5efe0a67bd..231f1e1c58 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,7 @@ if test "x$asan_enabled" = "xtrue"; then CFLAGS_ASAN+=" -fsanitize=address" CFLAGS_ASAN+=" -DASAN_ENABLED" CFLAGS_ASAN+=" -ggdb -fno-omit-frame-pointer -U_FORTIFY_SOURCE" + CFLAGS_ASAN+=" -Wno-maybe-uninitialized" AC_SUBST(CFLAGS_ASAN) LDFLAGS_ASAN+=" -lasan" diff --git a/fdbsyncd/fdbsync.cpp b/fdbsyncd/fdbsync.cpp index 0d71f721dc..3c1fae145a 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -324,7 +324,7 @@ void FdbSync::updateLocalMac (struct m_fdb_info *info) if (fdb_type == FDB_TYPE_DYNAMIC) { - type = "dynamic"; + type = "dynamic extern_learn"; } else { @@ -384,7 +384,7 @@ void FdbSync::addLocalMac(string key, string op) if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) { - type = "dynamic"; + type = "dynamic extern_learn"; } else { @@ -432,7 +432,7 @@ void FdbSync::updateMclagRemoteMac (struct m_fdb_info *info) if (fdb_type == FDB_TYPE_DYNAMIC) { - type = "dynamic"; + type = "dynamic extern_learn"; } else { @@ -511,7 +511,7 @@ void FdbSync::macRefreshStateDB(int vlan, string kmac) if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) { - type = "dynamic"; + type = "dynamic extern_learn"; } else { diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index caf6210084..0f6ee41188 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -465,6 +465,7 @@ void RouteSync::onEvpnRouteMsg(struct nlmsghdr *h, int len) inet_ntop(rtm->rtm_family, dstaddr, buf, MAX_ADDR_SIZE), dst_len); } + auto proto_str = getProtocolString(rtm->rtm_protocol); SWSS_LOG_INFO("Receive route message dest ip prefix: %s Op:%s", destipprefix, nlmsg_type == RTM_NEWROUTE ? "add":"del"); @@ -550,17 +551,20 @@ void RouteSync::onEvpnRouteMsg(struct nlmsghdr *h, int len) FieldValueTuple intf("ifname", intf_list); FieldValueTuple vni("vni_label", vni_list); FieldValueTuple mac("router_mac", mac_list); + FieldValueTuple proto("protocol", proto_str); fvVector.push_back(nh); fvVector.push_back(intf); fvVector.push_back(vni); fvVector.push_back(mac); + fvVector.push_back(proto); if (!warmRestartInProgress) { m_routeTable.set(destipprefix, fvVector); - SWSS_LOG_DEBUG("RouteTable set msg: %s vtep:%s vni:%s mac:%s intf:%s", - destipprefix, nexthops.c_str(), vni_list.c_str(), mac_list.c_str(), intf_list.c_str()); + SWSS_LOG_DEBUG("RouteTable set msg: %s vtep:%s vni:%s mac:%s intf:%s protocol:%s", + destipprefix, nexthops.c_str(), vni_list.c_str(), mac_list.c_str(), intf_list.c_str(), + proto_str.c_str()); } /* diff --git a/fpmsyncd/routesync.h b/fpmsyncd/routesync.h index fd18b9d25a..eb07eb8f15 100644 --- a/fpmsyncd/routesync.h +++ b/fpmsyncd/routesync.h @@ -115,7 +115,7 @@ class RouteSync : public NetMsg string& mac_list, string& intf_list, string rmac, string vlan_id); - bool getEvpnNextHop(struct nlmsghdr *h, int received_bytes, struct rtattr *tb[], + virtual bool getEvpnNextHop(struct nlmsghdr *h, int received_bytes, struct rtattr *tb[], string& nexthops, string& vni_list, string& mac_list, string& intf_list); diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 6af8189c95..e7743ab44d 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -113,7 +113,8 @@ orchagent_SOURCES = \ dash/dashaclorch.cpp \ dash/dashaclgroupmgr.cpp \ dash/dashtagmgr.cpp \ - dash/pbutils.cpp + dash/pbutils.cpp \ + twamporch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 6906744cc2..5ad908f082 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -2586,6 +2586,12 @@ bool AclTable::add(shared_ptr newRule) if (ruleIter != rules.end()) { // If ACL rule already exists, delete it first + if (ruleIter->second->hasCounter()) + { + // Deregister the flex counter before deleting the rule + // A new flex counter will be created when the new rule is added + m_pAclOrch->deregisterFlexCounter(*(ruleIter->second)); + } if (ruleIter->second->remove()) { rules.erase(ruleIter); diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index 91f13578a6..c3a63c5ec3 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -966,8 +966,10 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) return handle_status; } } - // create/remove a port queue counter for the queue buffer - else + // create/remove a port queue counter for the queue buffer. + // For VOQ chassis, flexcounterorch adds the Queue Counters for all egress and VOQ queues of all front panel and system ports + // to the FLEX_COUNTER_DB irrespective of BUFFER_QUEUE configuration. So Port Queue counter needs to be updated only for non VOQ switch. + else if (gMySwitchType != "voq") { auto flexCounterOrch = gDirectory.get(); auto queues = tokens[1]; diff --git a/orchagent/bulker.h b/orchagent/bulker.h index dcbc134d3b..86308329b9 100644 --- a/orchagent/bulker.h +++ b/orchagent/bulker.h @@ -96,6 +96,14 @@ static inline bool operator==(const sai_inseg_entry_t& a, const sai_inseg_entry_ ; } +static inline bool operator==(const sai_neighbor_entry_t& a, const sai_neighbor_entry_t& b) +{ + return a.switch_id == b.switch_id + && a.rif_id == b.rif_id + && a.ip_address == b.ip_address + ; +} + static inline bool operator==(const sai_inbound_routing_entry_t& a, const sai_inbound_routing_entry_t& b) { return a.switch_id == b.switch_id @@ -203,6 +211,19 @@ namespace std } }; + template <> + struct hash + { + size_t operator()(const sai_neighbor_entry_t& a) const noexcept + { + size_t seed = 0; + boost::hash_combine(seed, a.switch_id); + boost::hash_combine(seed, a.rif_id); + boost::hash_combine(seed, a.ip_address); + return seed; + } + }; + template <> struct hash { @@ -334,6 +355,19 @@ struct SaiBulkerTraits using bulk_set_entry_attribute_fn = sai_bulk_set_inseg_entry_attribute_fn; }; +template<> +struct SaiBulkerTraits +{ + using entry_t = sai_neighbor_entry_t; + using api_t = sai_neighbor_api_t; + using create_entry_fn = sai_create_neighbor_entry_fn; + using remove_entry_fn = sai_remove_neighbor_entry_fn; + using set_entry_attribute_fn = sai_set_neighbor_entry_attribute_fn; + using bulk_create_entry_fn = sai_bulk_create_neighbor_entry_fn; + using bulk_remove_entry_fn = sai_bulk_remove_neighbor_entry_fn; + using bulk_set_entry_attribute_fn = sai_bulk_set_neighbor_entry_attribute_fn; +}; + template<> struct SaiBulkerTraits { @@ -811,6 +845,15 @@ inline EntityBulker::EntityBulker(sai_mpls_api_t *api, size_t ma set_entries_attribute = api->set_inseg_entries_attribute; } +template <> +inline EntityBulker::EntityBulker(sai_neighbor_api_t *api, size_t max_bulk_size) : + max_bulk_size(max_bulk_size) +{ + create_entries = api->create_neighbor_entries; + remove_entries = api->remove_neighbor_entries; + set_entries_attribute = api->set_neighbor_entries_attribute; +} + template <> inline EntityBulker::EntityBulker(sai_dash_inbound_routing_api_t *api, size_t max_bulk_size) : max_bulk_size(max_bulk_size) { diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 966ab96dd9..b5844bbea3 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -64,6 +64,7 @@ const map crmResTypeNameMap = { CrmResourceType::CRM_DASH_IPV6_ACL_GROUP, "DASH_IPV6_ACL_GROUP" }, { CrmResourceType::CRM_DASH_IPV4_ACL_RULE, "DASH_IPV4_ACL_RULE" }, { CrmResourceType::CRM_DASH_IPV6_ACL_RULE, "DASH_IPV6_ACL_RULE" }, + { CrmResourceType::CRM_TWAMP_ENTRY, "TWAMP_ENTRY" } }; const map crmResSaiAvailAttrMap = @@ -84,6 +85,7 @@ const map crmResSaiAvailAttrMap = { CrmResourceType::CRM_IPMC_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_IPMC_ENTRY}, { CrmResourceType::CRM_SNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY }, { CrmResourceType::CRM_DNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY }, + { CrmResourceType::CRM_TWAMP_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_TWAMP_SESSION } }; const map crmResSaiObjAttrMap = @@ -125,6 +127,7 @@ const map crmResSaiObjAttrMap = { CrmResourceType::CRM_DASH_IPV6_ACL_GROUP, (sai_object_type_t)SAI_OBJECT_TYPE_DASH_ACL_GROUP }, { CrmResourceType::CRM_DASH_IPV4_ACL_RULE, (sai_object_type_t)SAI_OBJECT_TYPE_DASH_ACL_RULE }, { CrmResourceType::CRM_DASH_IPV6_ACL_RULE, (sai_object_type_t)SAI_OBJECT_TYPE_DASH_ACL_RULE }, + { CrmResourceType::CRM_TWAMP_ENTRY, SAI_OBJECT_TYPE_NULL } }; const map crmResAddrFamilyAttrMap = @@ -185,7 +188,8 @@ const map crmThreshTypeResMap = { "dash_ipv4_acl_group_threshold_type", CrmResourceType::CRM_DASH_IPV4_ACL_GROUP }, { "dash_ipv6_acl_group_threshold_type", CrmResourceType::CRM_DASH_IPV6_ACL_GROUP }, { "dash_ipv4_acl_rule_threshold_type", CrmResourceType::CRM_DASH_IPV4_ACL_RULE }, - { "dash_ipv6_acl_rule_threshold_type", CrmResourceType::CRM_DASH_IPV6_ACL_RULE } + { "dash_ipv6_acl_rule_threshold_type", CrmResourceType::CRM_DASH_IPV6_ACL_RULE }, + { "twamp_entry_threshold_type", CrmResourceType::CRM_TWAMP_ENTRY } }; const map crmThreshLowResMap = @@ -226,7 +230,8 @@ const map crmThreshLowResMap = { "dash_ipv4_acl_group_low_threshold", CrmResourceType::CRM_DASH_IPV4_ACL_GROUP }, { "dash_ipv6_acl_group_low_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_GROUP }, { "dash_ipv4_acl_rule_low_threshold", CrmResourceType::CRM_DASH_IPV4_ACL_RULE }, - { "dash_ipv6_acl_rule_low_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_RULE } + { "dash_ipv6_acl_rule_low_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_RULE }, + { "twamp_entry_low_threshold", CrmResourceType::CRM_TWAMP_ENTRY } }; const map crmThreshHighResMap = @@ -267,7 +272,8 @@ const map crmThreshHighResMap = { "dash_ipv4_acl_group_high_threshold", CrmResourceType::CRM_DASH_IPV4_ACL_GROUP }, { "dash_ipv6_acl_group_high_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_GROUP }, { "dash_ipv4_acl_rule_high_threshold", CrmResourceType::CRM_DASH_IPV4_ACL_RULE }, - { "dash_ipv6_acl_rule_high_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_RULE } + { "dash_ipv6_acl_rule_high_threshold", CrmResourceType::CRM_DASH_IPV6_ACL_RULE }, + { "twamp_entry_high_threshold", CrmResourceType::CRM_TWAMP_ENTRY } }; const map crmThreshTypeMap = @@ -315,7 +321,8 @@ const map crmAvailCntsTableMap = { "crm_stats_dash_ipv4_acl_group_available", CrmResourceType::CRM_DASH_IPV4_ACL_GROUP }, { "crm_stats_dash_ipv6_acl_group_available", CrmResourceType::CRM_DASH_IPV6_ACL_GROUP }, { "crm_stats_dash_ipv4_acl_rule_available", CrmResourceType::CRM_DASH_IPV4_ACL_RULE }, - { "crm_stats_dash_ipv6_acl_rule_available", CrmResourceType::CRM_DASH_IPV6_ACL_RULE } + { "crm_stats_dash_ipv6_acl_rule_available", CrmResourceType::CRM_DASH_IPV6_ACL_RULE }, + { "crm_stats_twamp_entry_available", CrmResourceType::CRM_TWAMP_ENTRY } }; const map crmUsedCntsTableMap = @@ -356,7 +363,8 @@ const map crmUsedCntsTableMap = { "crm_stats_dash_ipv4_acl_group_used", CrmResourceType::CRM_DASH_IPV4_ACL_GROUP }, { "crm_stats_dash_ipv6_acl_group_used", CrmResourceType::CRM_DASH_IPV6_ACL_GROUP }, { "crm_stats_dash_ipv4_acl_rule_used", CrmResourceType::CRM_DASH_IPV4_ACL_RULE }, - { "crm_stats_dash_ipv6_acl_rule_used", CrmResourceType::CRM_DASH_IPV6_ACL_RULE } + { "crm_stats_dash_ipv6_acl_rule_used", CrmResourceType::CRM_DASH_IPV6_ACL_RULE }, + { "crm_stats_twamp_entry_used", CrmResourceType::CRM_TWAMP_ENTRY }, }; CrmOrch::CrmOrch(DBConnector *db, string tableName): @@ -877,6 +885,7 @@ void CrmOrch::getResAvailableCounters() case CrmResourceType::CRM_DASH_IPV6_OUTBOUND_CA_TO_PA: case CrmResourceType::CRM_DASH_IPV4_ACL_GROUP: case CrmResourceType::CRM_DASH_IPV6_ACL_GROUP: + case CrmResourceType::CRM_TWAMP_ENTRY: { getResAvailability(res.first, res.second); break; diff --git a/orchagent/crmorch.h b/orchagent/crmorch.h index 9eb6001185..961bfaebe4 100644 --- a/orchagent/crmorch.h +++ b/orchagent/crmorch.h @@ -49,7 +49,8 @@ enum class CrmResourceType CRM_DASH_IPV4_ACL_GROUP, CRM_DASH_IPV6_ACL_GROUP, CRM_DASH_IPV4_ACL_RULE, - CRM_DASH_IPV6_ACL_RULE + CRM_DASH_IPV6_ACL_RULE, + CRM_TWAMP_ENTRY }; enum class CrmThresholdType diff --git a/orchagent/dash/dashorch.cpp b/orchagent/dash/dashorch.cpp index 95dde9f888..d7c7818e5a 100644 --- a/orchagent/dash/dashorch.cpp +++ b/orchagent/dash/dashorch.cpp @@ -277,9 +277,11 @@ bool DashOrch::setEniAdminState(const string& eni, const EniEntry& entry) { SWSS_LOG_ENTER(); + bool eni_enable = entry.metadata.admin_state() == dash::eni::State::STATE_ENABLED; + sai_attribute_t eni_attr; eni_attr.id = SAI_ENI_ATTR_ADMIN_STATE; - eni_attr.value.booldata = entry.metadata.admin_state(); + eni_attr.value.booldata = eni_enable; sai_status_t status = sai_dash_eni_api->set_eni_attribute(eni_entries_[eni].eni_id, &eni_attr); @@ -293,7 +295,7 @@ bool DashOrch::setEniAdminState(const string& eni, const EniEntry& entry) } } eni_entries_[eni].metadata.set_admin_state(entry.metadata.admin_state()); - SWSS_LOG_NOTICE("Set ENI %s admin state to %s", eni.c_str(), entry.metadata.admin_state() ? "UP" : "DOWN"); + SWSS_LOG_NOTICE("Set ENI %s admin state to %s", eni.c_str(), eni_enable ? "UP" : "DOWN"); return true; } diff --git a/orchagent/fabricportsorch.cpp b/orchagent/fabricportsorch.cpp index 798a62988c..b46fcede09 100644 --- a/orchagent/fabricportsorch.cpp +++ b/orchagent/fabricportsorch.cpp @@ -10,6 +10,8 @@ #include "sai_serialize.h" #include "timer.h" #include "saihelper.h" +#include "converter.h" +#include "stringutility.h" #define FABRIC_POLLING_INTERVAL_DEFAULT (30) #define FABRIC_PORT_PREFIX "PORT" @@ -19,6 +21,20 @@ #define FABRIC_PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000 #define FABRIC_QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP "FABRIC_QUEUE_STAT_COUNTER" #define FABRIC_QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 100000 +#define FABRIC_DEBUG_POLLING_INTERVAL_DEFAULT (60) +#define FABRIC_MONITOR_DATA "FABRIC_MONITOR_DATA" +#define APPL_FABRIC_PORT_PREFIX "Fabric" + +// constants for link monitoring +#define MAX_SKIP_CRCERR_ON_LNKUP_POLLS 20 +#define MAX_SKIP_FECERR_ON_LNKUP_POLLS 20 +// the follow constants will be replaced with the number in config_db +#define FEC_ISOLATE_POLLS 2 +#define FEC_UNISOLATE_POLLS 8 +#define ISOLATION_POLLS_CFG 1 +#define RECOVERY_POLLS_CFG 8 +#define ERROR_RATE_CRC_CELLS_CFG 1 +#define ERROR_RATE_RX_CELLS_CFG 61035156 extern sai_object_id_t gSwitchId; extern sai_switch_api_t *sai_switch_api; @@ -51,7 +67,8 @@ FabricPortsOrch::FabricPortsOrch(DBConnector *appl_db, vector(new DBConnector("COUNTERS_DB", 0)); m_portNameQueueCounterTable = unique_ptr(new Table(m_counter_db.get(), COUNTERS_FABRIC_QUEUE_NAME_MAP)); m_portNamePortCounterTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_FABRIC_PORT_NAME_MAP)); + m_fabricCounterTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_TABLE)); m_flex_db = shared_ptr(new DBConnector("FLEX_COUNTER_DB", 0)); m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), APP_FABRIC_PORT_TABLE_NAME)); + m_appl_db = shared_ptr(new DBConnector("APPL_DB", 0)); + m_applTable = unique_ptr
(new Table(m_appl_db.get(), APP_FABRIC_MONITOR_PORT_TABLE_NAME)); + m_applMonitorConstTable = unique_ptr
(new Table(m_appl_db.get(), APP_FABRIC_MONITOR_DATA_TABLE_NAME)); m_fabricPortStatEnabled = fabricPortStatEnabled; m_fabricQueueStatEnabled = fabricQueueStatEnabled; @@ -75,6 +96,10 @@ FabricPortsOrch::FabricPortsOrch(DBConnector *appl_db, vectorstart(); + + auto debug_executor = new ExecutableTimer(m_debugTimer, this, "FABRIC_DEBUG_POLL"); + Orch::addExecutor(debug_executor); + m_debugTimer->start(); } int FabricPortsOrch::getFabricPortList() @@ -336,25 +361,684 @@ void FabricPortsOrch::updateFabricPortState() } } +void FabricPortsOrch::updateFabricDebugCounters() +{ + if (!m_getFabricPortListDone) return; + + SWSS_LOG_ENTER(); + + // Get time + time_t now; + struct timespec time_now; + if (clock_gettime(CLOCK_MONOTONIC, &time_now) < 0) + { + return; + } + now = time_now.tv_sec; + + int fecIsolatedPolls = FEC_ISOLATE_POLLS; // monPollThreshIsolation + int fecUnisolatePolls = FEC_UNISOLATE_POLLS; // monPollThreshRecovery + int isolationPollsCfg = ISOLATION_POLLS_CFG; // monPollThreshIsolation + int recoveryPollsCfg = RECOVERY_POLLS_CFG; // monPollThreshRecovery + int errorRateCrcCellsCfg = ERROR_RATE_CRC_CELLS_CFG; // monErrThreshCrcCells + int errorRateRxCellsCfg = ERROR_RATE_RX_CELLS_CFG; // monErrThreshRxCells + string applConstKey = FABRIC_MONITOR_DATA; + std::vector constValues; + SWSS_LOG_INFO("updateFabricDebugCounters"); + + bool setCfgVal = m_applMonitorConstTable->get("FABRIC_MONITOR_DATA", constValues); + if (!setCfgVal) + { + SWSS_LOG_INFO("applConstKey %s default values not set", applConstKey.c_str()); + } + else + { + SWSS_LOG_INFO("applConstKey %s default values get set", applConstKey.c_str()); + } + string configVal = "1"; + for (auto cv : constValues) + { + configVal = fvValue(cv); + if (fvField(cv) == "monErrThreshCrcCells") + { + errorRateCrcCellsCfg = stoi(configVal); + SWSS_LOG_INFO("monErrThreshCrcCells: %s %s", configVal.c_str(), fvField(cv).c_str()); + continue; + } + if (fvField(cv) == "monErrThreshRxCells") + { + errorRateRxCellsCfg = stoi(configVal); + SWSS_LOG_INFO("monErrThreshRxCells: %s %s", configVal.c_str(), fvField(cv).c_str()); + continue; + } + if (fvField(cv) == "monPollThreshIsolation") + { + fecIsolatedPolls = stoi(configVal); + isolationPollsCfg = stoi(configVal); + SWSS_LOG_INFO("monPollThreshIsolation: %s %s", configVal.c_str(), fvField(cv).c_str()); + continue; + } + if (fvField(cv) == "monPollThreshRecovery") + { + fecUnisolatePolls = stoi(configVal); + recoveryPollsCfg = stoi(configVal); + SWSS_LOG_INFO("monPollThreshRecovery: %s", configVal.c_str()); + continue; + } + } + + // Get debug countesrs (e.g. # of cells with crc errors, # of cells) + for (auto p : m_fabricLanePortMap) + { + int lane = p.first; + sai_object_id_t port = p.second; + + string key = FABRIC_PORT_PREFIX + to_string(lane); + // so basically port is the oid + vector fieldValues; + static const array cntNames = + { + "SAI_PORT_STAT_IF_IN_ERRORS", // cells with crc errors + "SAI_PORT_STAT_IF_IN_FABRIC_DATA_UNITS", // rx data cells + "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES" // cell with uncorrectable errors + }; + if (!m_fabricCounterTable->get(sai_serialize_object_id(port), fieldValues)) + { + SWSS_LOG_INFO("no port %s", sai_serialize_object_id(port).c_str()); + } + + uint64_t rxCells = 0; + uint64_t crcErrors = 0; + uint64_t codeErrors = 0; + for (const auto& fv : fieldValues) + { + const auto field = fvField(fv); + const auto value = fvValue(fv); + for (size_t cnt = 0; cnt != cntNames.size(); cnt++) + { + if (field == "SAI_PORT_STAT_IF_IN_ERRORS") + { + crcErrors = stoull(value); + } + else if (field == "SAI_PORT_STAT_IF_IN_FABRIC_DATA_UNITS") + { + rxCells = stoull(value); + } + else if (field == "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES") + { + codeErrors = stoull(value); + } + SWSS_LOG_INFO("port %s %s %lld %lld %lld at %s", + sai_serialize_object_id(port).c_str(), field.c_str(), (long long)crcErrors, + (long long)rxCells, (long long)codeErrors, asctime(gmtime(&now))); + } + } + // now we get the values of: + // *totalNumCells *cellsWithCrcErrors *cellsWithUncorrectableErrors + // + // Check if the error rate (crcErrors/numRxCells) is greater than configured error threshold + // (errorRateCrcCellsCfg/errorRateRxCellsCfg). + // This is changing to check (crcErrors * errorRateRxCellsCfg) > (numRxCells * errorRateCrcCellsCfg) + // Default value is: (crcErrors * 61035156) > (numRxCells * 1) + // numRxCells = snmpBcmRxDataCells + snmpBcmRxControlCells + // As we don't have snmpBcmRxControlCells polled right now, + // we can use snmpBcmRxDataCells only and add snmpBcmRxControlCells later when it is getting polled. + // + // In STATE_DB, add several new attribute for each port: + // consecutivePollsWithErrors POLL_WITH_ERRORS + // consecutivePollsWithNoErrors POLL_WITH_NO_ERRORS + // consecutivePollsWithFecErrs POLL_WITH_FEC_ERRORS + // consecutivePollsWithNoFecErrs POLL_WITH_NOFEC_ERRORS + // + // skipErrorsOnLinkupCount SKIP_ERR_ON_LNKUP_CNT -- for skip all errors during boot up time + // skipCrcErrorsOnLinkupCount SKIP_CRC_ERR_ON_LNKUP_CNT + // skipFecErrorsOnLinkupCount SKIP_FEC_ERR_ON_LNKUP_CNT + // removeProblemLinkCount RM_PROBLEM_LNK_CNT -- this is for feature of remove a flaky link permanently + // + // cfgIsolated CONFIG_ISOLATED + + int consecutivePollsWithErrors = 0; + int consecutivePollsWithNoErrors = 0; + int consecutivePollsWithFecErrs = 0; + int consecutivePollsWithNoFecErrs = 0; + + int skipCrcErrorsOnLinkupCount = 0; + int skipFecErrorsOnLinkupCount = 0; + uint64_t prevRxCells = 0; + uint64_t prevCrcErrors = 0; + uint64_t prevCodeErrors = 0; + + uint64_t testCrcErrors = 0; + uint64_t testCodeErrors = 0; + + int autoIsolated = 0; + int cfgIsolated = 0; + int isolated = 0; + string lnkStatus = "down"; + string testState = "product"; + + // Get appl_db values, and update state_db later with other attributes + string applKey = APPL_FABRIC_PORT_PREFIX + to_string(lane); + std::vector applValues; + string applResult = "False"; + bool exist = m_applTable->get(applKey, applValues); + if (!exist) + { + SWSS_LOG_NOTICE("No app infor for port %s", applKey.c_str()); + } + else + { + for (auto v : applValues) + { + applResult = fvValue(v); + if (fvField(v) == "isolateStatus") + { + if (applResult == "True") + { + cfgIsolated = 1; + } + else + { + cfgIsolated = 0; + } + SWSS_LOG_INFO("Port %s isolateStatus: %s %d", + applKey.c_str(), applResult.c_str(), cfgIsolated); + } + } + } + + // Get the consecutive polls from the state db + std::vector values; + string valuePt; + exist = m_stateTable->get(key, values); + if (!exist) + { + SWSS_LOG_INFO("No state infor for port %s", key.c_str()); + return; + } + for (auto val : values) + { + valuePt = fvValue(val); + if (fvField(val) == "STATUS") + { + lnkStatus = valuePt; + continue; + } + if (fvField(val) == "POLL_WITH_ERRORS") + { + consecutivePollsWithErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "POLL_WITH_NO_ERRORS") + { + consecutivePollsWithNoErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "POLL_WITH_FEC_ERRORS") + { + consecutivePollsWithFecErrs = to_uint(valuePt); + continue; + } + if (fvField(val) == "POLL_WITH_NOFEC_ERRORS") + { + consecutivePollsWithNoFecErrs = to_uint(valuePt); + continue; + } + if (fvField(val) == "SKIP_CRC_ERR_ON_LNKUP_CNT") + { + skipCrcErrorsOnLinkupCount = to_uint(valuePt); + continue; + } + if (fvField(val) == "SKIP_FEC_ERR_ON_LNKUP_CNT") + { + skipFecErrorsOnLinkupCount = to_uint(valuePt); + continue; + } + if (fvField(val) == "RX_CELLS") + { + prevRxCells = to_uint(valuePt); + continue; + } + if (fvField(val) == "CRC_ERRORS") + { + prevCrcErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "CODE_ERRORS") + { + prevCodeErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "AUTO_ISOLATED") + { + autoIsolated = to_uint(valuePt); + SWSS_LOG_INFO("port %s currently isolated: %s", key.c_str(),valuePt.c_str()); + continue; + } + if (fvField(val) == "TEST_CRC_ERRORS") + { + testCrcErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "TEST_CODE_ERRORS") + { + testCodeErrors = to_uint(valuePt); + continue; + } + if (fvField(val) == "TEST") + { + testState = valuePt; + continue; + } + } + + // checking crc errors + int maxSkipCrcCnt = MAX_SKIP_CRCERR_ON_LNKUP_POLLS; + if (testState == "TEST"){ + maxSkipCrcCnt = 2; + } + if (skipCrcErrorsOnLinkupCount < maxSkipCrcCnt) + { + skipCrcErrorsOnLinkupCount += 1; + valuePt = to_string(skipCrcErrorsOnLinkupCount); + m_stateTable->hset(key, "SKIP_CRC_ERR_ON_LNKUP_CNT", valuePt.c_str()); + SWSS_LOG_INFO("port %s updates SKIP_CRC_ERR_ON_LNKUP_CNT to %s %d", + key.c_str(), valuePt.c_str(), skipCrcErrorsOnLinkupCount); + // update error counters. + prevCrcErrors = crcErrors; + } + else + { + uint64_t diffRxCells = 0; + uint64_t diffCrcCells = 0; + + diffRxCells = rxCells - prevRxCells; + if (testState == "TEST"){ + diffCrcCells = testCrcErrors - prevCrcErrors; + prevCrcErrors = 0; + isolationPollsCfg = isolationPollsCfg + 1; + } + else + { + diffCrcCells = crcErrors - prevCrcErrors; + prevCrcErrors = crcErrors; + } + bool isErrorRateMore = + ((diffCrcCells * errorRateRxCellsCfg) > + (diffRxCells * errorRateCrcCellsCfg)); + if (isErrorRateMore) + { + if (consecutivePollsWithErrors < isolationPollsCfg) + { + consecutivePollsWithErrors += 1; + consecutivePollsWithNoErrors = 0; + } + } else { + if (consecutivePollsWithNoErrors < recoveryPollsCfg) + { + consecutivePollsWithNoErrors += 1; + consecutivePollsWithErrors = 0; + } + } + SWSS_LOG_INFO("port %s diffCrcCells %lld", key.c_str(), (long long)diffCrcCells); + SWSS_LOG_INFO("consecutivePollsWithCRCErrs %d consecutivePollsWithNoCRCErrs %d", + consecutivePollsWithErrors, consecutivePollsWithNoErrors); + } + + // checking FEC errors + int maxSkipFecCnt = MAX_SKIP_FECERR_ON_LNKUP_POLLS; + if (testState == "TEST"){ + maxSkipFecCnt = 2; + } + if (skipFecErrorsOnLinkupCount < maxSkipFecCnt) + { + skipFecErrorsOnLinkupCount += 1; + valuePt = to_string(skipFecErrorsOnLinkupCount); + m_stateTable->hset(key, "SKIP_FEC_ERR_ON_LNKUP_CNT", valuePt.c_str()); + SWSS_LOG_INFO("port %s updates SKIP_FEC_ERR_ON_LNKUP_CNT to %s", + key.c_str(), valuePt.c_str()); + // update error counters + prevCodeErrors = codeErrors; + } + else + { + uint64_t diffCodeErrors = 0; + if (testState == "TEST"){ + diffCodeErrors = testCodeErrors - prevCodeErrors; + prevCodeErrors = 0; + fecIsolatedPolls = fecIsolatedPolls + 1; + } + else + { + diffCodeErrors = codeErrors - prevCodeErrors; + prevCodeErrors = codeErrors; + } + SWSS_LOG_INFO("port %s diffCodeErrors %lld", key.c_str(), (long long)diffCodeErrors); + if (diffCodeErrors > 0) + { + if (consecutivePollsWithFecErrs < fecIsolatedPolls) + { + consecutivePollsWithFecErrs += 1; + consecutivePollsWithNoFecErrs = 0; + } + } + else if (diffCodeErrors <= 0) + { + if (consecutivePollsWithNoFecErrs < fecUnisolatePolls) + { + consecutivePollsWithNoFecErrs += 1; + consecutivePollsWithFecErrs = 0; + } + } + SWSS_LOG_INFO("consecutivePollsWithFecErrs %d consecutivePollsWithNoFecErrs %d", + consecutivePollsWithFecErrs,consecutivePollsWithNoFecErrs); + SWSS_LOG_INFO("fecUnisolatePolls %d", fecUnisolatePolls); + } + + // take care serdes link shut state setting + if (lnkStatus == "up") + { + // debug information + SWSS_LOG_INFO("port %s status up autoIsolated %d", + key.c_str(), autoIsolated); + SWSS_LOG_INFO("consecutivePollsWithErrors %d consecutivePollsWithFecErrs %d", + consecutivePollsWithErrors, consecutivePollsWithFecErrs); + SWSS_LOG_INFO("consecutivePollsWithNoErrors %d consecutivePollsWithNoFecErrs %d", + consecutivePollsWithNoErrors, consecutivePollsWithNoFecErrs); + if (autoIsolated == 0 && (consecutivePollsWithErrors >= isolationPollsCfg + || consecutivePollsWithFecErrs >= fecIsolatedPolls)) + { + // Link needs to be isolated. + SWSS_LOG_INFO("port %s auto isolated", key.c_str()); + autoIsolated = 1; + valuePt = to_string(autoIsolated); + m_stateTable->hset(key, "AUTO_ISOLATED", valuePt); + SWSS_LOG_NOTICE("port %s set AUTO_ISOLATED %s", key.c_str(), valuePt.c_str()); + } + else if (autoIsolated == 1 && consecutivePollsWithNoErrors >= recoveryPollsCfg + && consecutivePollsWithNoFecErrs >= fecUnisolatePolls) + { + // Link is isolated, but no longer needs to be. + SWSS_LOG_INFO("port %s healthy again", key.c_str()); + autoIsolated = 0; + valuePt = to_string(autoIsolated); + m_stateTable->hset(key, "AUTO_ISOLATED", valuePt); + SWSS_LOG_INFO("port %s set AUTO_ISOLATED %s", key.c_str(), valuePt.c_str()); + } + if (cfgIsolated == 1) + { + isolated = 1; + SWSS_LOG_INFO("port %s keep isolated due to configuation",key.c_str()); + } + else + { + if (autoIsolated == 1) + { + isolated = 1; + SWSS_LOG_INFO("port %s keep isolated due to autoisolation",key.c_str()); + } + else + { + isolated = 0; + SWSS_LOG_INFO("port %s unisolated",key.c_str()); + } + } + // if "ISOLATED" is true, Call SAI api here to actually isolated the link + // if "ISOLATED" is false, Call SAP api to actually unisolate the link + } + else + { + SWSS_LOG_INFO("link down"); + } + + // Update state_db with new data + valuePt = to_string(consecutivePollsWithErrors); + m_stateTable->hset(key, "POLL_WITH_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set POLL_WITH_ERRORS %s", key.c_str(), valuePt.c_str()); + + valuePt = to_string(consecutivePollsWithNoErrors); + m_stateTable->hset(key, "POLL_WITH_NO_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set POLL_WITH_NO_ERRORS %s", key.c_str(), valuePt.c_str()); + + valuePt = to_string(consecutivePollsWithFecErrs); + m_stateTable->hset(key, "POLL_WITH_FEC_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set POLL_WITH_FEC_ERRORS %s", key.c_str(), valuePt.c_str()); + + valuePt = to_string(consecutivePollsWithNoFecErrs); + m_stateTable->hset(key, "POLL_WITH_NOFEC_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set POLL_WITH_NOFEC_ERRORS %s", + key.c_str(), valuePt.c_str()); + + valuePt = to_string(rxCells); + m_stateTable->hset(key, "RX_CELLS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set RX_CELLS %s", + key.c_str(), valuePt.c_str()); + + valuePt = to_string(prevCrcErrors); + m_stateTable->hset(key, "CRC_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set CRC_ERRORS %s", + key.c_str(), valuePt.c_str()); + + valuePt = to_string(prevCodeErrors); + m_stateTable->hset(key, "CODE_ERRORS", valuePt.c_str()); + SWSS_LOG_INFO("port %s set CODE_ERRORS %s", + key.c_str(), valuePt.c_str()); + + valuePt = to_string(cfgIsolated); + m_stateTable->hset(key, "CONFIG_ISOLATED", valuePt.c_str()); + SWSS_LOG_INFO("port %s set CONFIG_ISOLATED %s", + key.c_str(), valuePt.c_str()); + + valuePt = to_string(isolated); + m_stateTable->hset(key, "ISOLATED", valuePt.c_str()); + SWSS_LOG_INFO("port %s set ISOLATED %s", + key.c_str(), valuePt.c_str()); + } +} + void FabricPortsOrch::doTask() { } +void FabricPortsOrch::doFabricPortTask(Consumer &consumer) +{ + SWSS_LOG_NOTICE("FabricPortsOrch::doFabricPortTask"); + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + string alias, lanes; + string isolateStatus; + int forceIsolateCnt = 0; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "alias") + { + alias = fvValue(i); + } + else if (fvField(i) == "lanes") + { + lanes = fvValue(i); + } + else if (fvField(i) == "isolateStatus") + { + isolateStatus = fvValue(i); + } + else if (fvField(i) == "forceUnisolateStatus") + { + forceIsolateCnt = stoi(fvValue(i)); + } + } + // This method may be called with only some fields included. + // In that case read in the missing field data. + if (alias == "") + { + string new_alias; + SWSS_LOG_NOTICE("alias is NULL, key: %s", key.c_str()); + if (m_applTable->hget(key, "alias", new_alias)) + { + alias = new_alias; + SWSS_LOG_NOTICE("read new_alias, key: '%s', value: '%s'", key.c_str(), new_alias.c_str()); + } + else + { + SWSS_LOG_NOTICE("hget failed for key: %s, alias", key.c_str()); + } + } + if (lanes == "") + { + string new_lanes; + SWSS_LOG_NOTICE("lanes is NULL, key: %s", key.c_str()); + if (m_applTable->hget(key, "lanes", new_lanes)) + { + lanes = new_lanes; + SWSS_LOG_NOTICE("read new_lanes, key: '%s', value: '%s'", key.c_str(), new_lanes.c_str()); + } + else + { + SWSS_LOG_NOTICE("hget failed for key: %s, lanes", key.c_str()); + } + + } + if (isolateStatus == "") + { + string new_isolateStatus; + SWSS_LOG_NOTICE("isolateStatus is NULL, key: %s", key.c_str()); + if (m_applTable->hget(key, "isolateStatus", new_isolateStatus)) + { + isolateStatus = new_isolateStatus; + SWSS_LOG_NOTICE("read new_isolateStatus, key: '%s', value: '%s'", key.c_str(), new_isolateStatus.c_str()); + } + else + { + SWSS_LOG_NOTICE("hget failed for key: %s, isolateStatus", key.c_str()); + } + } + // Do not process if some data is still missing. + if (alias == "" || lanes == "" || isolateStatus == "" ) + { + SWSS_LOG_NOTICE("NULL values, skipping %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + SWSS_LOG_NOTICE("key %s alias %s isolateStatus %s lanes %s", + key.c_str(), alias.c_str(), isolateStatus.c_str(), lanes.c_str()); + // Call SAI api to isolate/unisolate the link here. + // Isolate the link if isolateStatus is True. + // Unisolate the link if isolateStatus is False. + + if (isolateStatus == "False") + { + // get state db value of forceIolatedCntInStateDb, + // if forceIolatedCnt != forceIolatedCntInStateDb + // 1) clear all isolate related flags in stateDb + // 2) replace the cnt in stateb + // + + std::vector values; + string state_key = FABRIC_PORT_PREFIX + lanes; + bool exist = m_stateTable->get(state_key, values); + if (!exist) + { + SWSS_LOG_NOTICE("React to unshut No state infor for port %s", state_key.c_str()); + } + else + { + SWSS_LOG_NOTICE("React to unshut port %s", state_key.c_str()); + } + int curVal = 0; + for (auto val : values) + { + if(fvField(val) == "FORCE_UN_ISOLATE") + { + curVal = stoi(fvValue(val)); + } + } + SWSS_LOG_INFO("Current %d Config %d", curVal, forceIsolateCnt); + if (curVal != forceIsolateCnt) + { + //update state_db; + string value_update; + value_update = to_string(forceIsolateCnt); + m_stateTable->hset(state_key, "FORCE_UN_ISOLATE", value_update.c_str()); + SWSS_LOG_NOTICE("port %s set FORCE_UN_ISOLATE %s", state_key.c_str(), value_update.c_str()); + + + // update all related fields in state_db: + // POLL_WITH_ERRORS 0 + m_stateTable->hset(state_key, "POLL_WITH_ERRORS", + m_defaultPollWithErrors.c_str()); + // POLL_WITH_NO_ERRORS 8 + m_stateTable->hset(state_key, "POLL_WITH_NO_ERRORS", + m_defaultPollWithNoErrors.c_str()); + // POLL_WITH_FEC_ERRORS 0 + m_stateTable->hset(state_key, "POLL_WITH_FEC_ERRORS", + m_defaultPollWithFecErrors.c_str()); + // POLL_WITH_NOFEC_ERRORS 8 + m_stateTable->hset(state_key, "POLL_WITH_NOFEC_ERRORS", + m_defaultPollWithNoFecErrors.c_str()); + // CONFIG_ISOLATED 0 + m_stateTable->hset(state_key, "CONFIG_ISOLATED", + m_defaultConfigIsolated.c_str()); + // ISOLATED 0 + m_stateTable->hset(state_key, "ISOLATED", + m_defaultIsolated.c_str()); + // AUTO_ISOLATED 0 + m_stateTable->hset(state_key, "AUTO_ISOLATED", + m_defaultAutoIsolated.c_str()); + } + } + } + it = consumer.m_toSync.erase(it); + } +} + void FabricPortsOrch::doTask(Consumer &consumer) { + SWSS_LOG_NOTICE("doTask from FabricPortsOrch"); + + string table_name = consumer.getTableName(); + + if (table_name == APP_FABRIC_MONITOR_PORT_TABLE_NAME) + { + doFabricPortTask(consumer); + } } void FabricPortsOrch::doTask(swss::SelectableTimer &timer) { SWSS_LOG_ENTER(); - if (!m_getFabricPortListDone) + if (timer.getFd() == m_timer->getFd()) { - getFabricPortList(); - } + if (!m_getFabricPortListDone) + { + getFabricPortList(); + } - if (m_getFabricPortListDone) + if (m_getFabricPortListDone) + { + updateFabricPortState(); + } + } + else if (timer.getFd() == m_debugTimer->getFd()) { - updateFabricPortState(); + if (!m_getFabricPortListDone) + { + // Skip collecting debug information + // as we don't have all fabric ports yet. + return; + } + + if (m_getFabricPortListDone) + { + updateFabricDebugCounters(); + } } } diff --git a/orchagent/fabricportsorch.h b/orchagent/fabricportsorch.h index de7ee7a7b0..0d637dec43 100644 --- a/orchagent/fabricportsorch.h +++ b/orchagent/fabricportsorch.h @@ -24,13 +24,18 @@ class FabricPortsOrch : public Orch, public Subject shared_ptr m_state_db; shared_ptr m_counter_db; shared_ptr m_flex_db; + shared_ptr m_appl_db; unique_ptr
m_stateTable; unique_ptr
m_portNameQueueCounterTable; unique_ptr
m_portNamePortCounterTable; + unique_ptr
m_fabricCounterTable; + unique_ptr
m_applTable; + unique_ptr
m_applMonitorConstTable; unique_ptr m_flexCounterTable; swss::SelectableTimer *m_timer = nullptr; + swss::SelectableTimer *m_debugTimer = nullptr; FlexCounterManager port_stat_manager; FlexCounterManager queue_stat_manager; @@ -43,12 +48,23 @@ class FabricPortsOrch : public Orch, public Subject bool m_getFabricPortListDone = false; bool m_isQueueStatsGenerated = false; + + string m_defaultPollWithErrors = "0"; + string m_defaultPollWithNoErrors = "8"; + string m_defaultPollWithFecErrors = "0"; + string m_defaultPollWithNoFecErrors = "8"; + string m_defaultConfigIsolated = "0"; + string m_defaultIsolated = "0"; + string m_defaultAutoIsolated = "0"; + int getFabricPortList(); void generatePortStats(); void updateFabricPortState(); + void updateFabricDebugCounters(); void doTask() override; void doTask(Consumer &consumer); + void doFabricPortTask(Consumer &consumer); void doTask(swss::SelectableTimer &timer); }; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 4363beb9ea..20d0bc09bc 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -37,8 +37,9 @@ extern bool gIsNatSupported; extern NeighOrch *gNeighOrch; extern string gMySwitchType; extern int32_t gVoqMySwitchId; +extern RouteOrch *gRouteOrch; -const int intfsorch_pri = 35; +const int IntfsOrch::intfsorch_pri = 35; #define RIF_FLEX_STAT_COUNTER_POLL_MSECS "1000" #define UPDATE_MAPS_SEC 1 @@ -57,8 +58,8 @@ static const vector rifStatIds = SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS, }; -IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch, DBConnector *chassisAppDb) : - Orch(db, tableName, intfsorch_pri), m_vrfOrch(vrf_orch) +IntfsOrch::IntfsOrch(DBConnector *db, vector tableNames, VRFOrch *vrf_orch, DBConnector *chassisAppDb) : + Orch(db, tableNames), m_vrfOrch(vrf_orch) { SWSS_LOG_ENTER(); @@ -105,7 +106,7 @@ IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch, DBCon if(gMySwitchType == "voq") { //Add subscriber to process VOQ system interface - tableName = CHASSIS_APP_SYSTEM_INTERFACE_TABLE_NAME; + string tableName = CHASSIS_APP_SYSTEM_INTERFACE_TABLE_NAME; Orch::addExecutor(new Consumer(new SubscriberStateTable(chassisAppDb, tableName, TableConsumable::DEFAULT_POP_BATCH_SIZE, 0), this, tableName)); m_tableVoqSystemInterfaceTable = unique_ptr
(new Table(chassisAppDb, CHASSIS_APP_SYSTEM_INTERFACE_TABLE_NAME)); } @@ -368,6 +369,21 @@ bool IntfsOrch::setIntfVlanFloodType(const Port &port, sai_vlan_flood_control_ty } } + // Also set ipv6 multicast flood type + attr.id = SAI_VLAN_ATTR_UNKNOWN_MULTICAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = vlan_flood_type; + + status = sai_vlan_api->set_vlan_attribute(port.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set multicast flood type for VLAN %u, rv:%d", port.m_vlan_info.vlan_id, status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_VLAN, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + return true; } @@ -488,6 +504,17 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre intfs_entry.ref_count = 0; intfs_entry.proxy_arp = false; intfs_entry.vrf_id = vrf_id; + + // use the configured MAC address for setting router interface's attribute via SAI api + // or use the system's MAC address instead + if (port.m_mac) + { + intfs_entry.mac = port.m_mac; + } + else + { + intfs_entry.mac = gMacAddress; + } m_syncdIntfses[alias] = intfs_entry; m_vrfOrch->increaseVrfRefCount(vrf_id); } @@ -656,7 +683,6 @@ void IntfsOrch::doTask(Consumer &consumer) } string table_name = consumer.getTableName(); - auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { @@ -681,7 +707,7 @@ void IntfsOrch::doTask(Consumer &consumer) ip_prefix_in_key = true; } - if(table_name == CHASSIS_APP_SYSTEM_INTERFACE_TABLE_NAME) + if (table_name == CHASSIS_APP_SYSTEM_INTERFACE_TABLE_NAME) { if(isLocalSystemPortIntf(alias)) { @@ -691,6 +717,17 @@ void IntfsOrch::doTask(Consumer &consumer) } } + //TODO: consider to refactor for different private function + if (table_name == APP_SAG_TABLE_NAME) + { + const vector& data = kfvFieldsValues(t); + string op = kfvOp(t); + doSagTask(data, op); + + it = consumer.m_toSync.erase(it); + continue; + } + const vector& data = kfvFieldsValues(t); string vrf_name = "", vnet_name = "", nat_zone = ""; MacAddress mac; @@ -962,38 +999,48 @@ void IntfsOrch::doTask(Consumer &consumer) } } - if (mac) + if (!mac) { - /* Get mac information and update mac of the interface*/ - sai_attribute_t attr; - attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; - memcpy(attr.value.mac, mac.getMac(), sizeof(sai_mac_t)); + mac = gMacAddress; + } - /*port.m_rif_id is set in setIntf(), need get port again*/ - if (gPortsOrch->getPort(alias, port)) + // update mac if it is changed + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) + { + if (m_syncdIntfses[alias].mac != mac) { - sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); - if (status != SAI_STATUS_SUCCESS) + // port.m_rif_id is set in setIntf(), need to get port again + if (gPortsOrch->getPort(alias, port)) { - SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, rv:%d", - mac.to_string().c_str(), port.m_alias.c_str(), status); - if (handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status) == task_need_retry) + sai_attribute_t attr; + attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; + + memcpy(attr.value.mac, mac.getMac(), sizeof(sai_mac_t)); + sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); + + if (status != SAI_STATUS_SUCCESS) { - it++; - continue; + SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, rv:%d", + mac.to_string().c_str(), port.m_alias.c_str(), status); + if (handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status) == task_need_retry) + { + it++; + continue; + } + } + else + { + SWSS_LOG_NOTICE("Set router interface mac %s for port %s success", + mac.to_string().c_str(), alias.c_str()); + m_syncdIntfses[alias].mac = mac; } } else { - SWSS_LOG_NOTICE("Set router interface mac %s for port %s success", - mac.to_string().c_str(), port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, get port fail", + mac.to_string().c_str(), alias.c_str()); } } - else - { - SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, getPort fail", - mac.to_string().c_str(), alias.c_str()); - } } if (!proxy_arp.empty()) @@ -1102,6 +1149,72 @@ void IntfsOrch::doTask(Consumer &consumer) } } +void IntfsOrch::doSagTask(vector data, const string& op) +{ + if (op == SET_COMMAND) + { + for (auto idx : data) + { + string sag_mac_str = ""; + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + SWSS_LOG_NOTICE("process key %s, value %s", field.c_str(), value.c_str()); + if (field == "gateway_mac") + { + sag_mac_str = value; + } + + if (!sag_mac_str.empty()) + { + MacAddress sag_mac; + try + { + sag_mac = MacAddress(sag_mac_str); + } + catch (const std::invalid_argument &e) + { + SWSS_LOG_ERROR("Invalid mac argument %s to %s()", sag_mac_str.c_str(), e.what()); + return; + } + + IpPrefix linklocal_prefix = gRouteOrch->getLinkLocalEui64Addr(sag_mac); + const vector& vrf_ids = m_vrfOrch->getVRFids(); + + // add link local route for default vrf + gRouteOrch->addLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu in default VRF", linklocal_prefix.to_string().c_str()); + + // add link local route for existed vrf + for (auto vrf_id: vrf_ids) + { + gRouteOrch->addLinkLocalRouteToMe(vrf_id, linklocal_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu in VRF %s", linklocal_prefix.to_string().c_str(), m_vrfOrch->getVRFname(vrf_id).c_str()); + } + + m_sagMac = sag_mac; + } + } + } + else if (op == DEL_COMMAND) + { + IpPrefix linklocal_prefix = gRouteOrch->getLinkLocalEui64Addr(m_sagMac); + const vector& vrf_ids = m_vrfOrch->getVRFids(); + + // remove link local route for default vrf + gRouteOrch->delLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); + SWSS_LOG_NOTICE("Removed link local ipv6 route %s to cpu from default VRF", linklocal_prefix.to_string().c_str()); + + // remove link local route for existed vrf + for (auto vrf_id: vrf_ids) + { + gRouteOrch->delLinkLocalRouteToMe(vrf_id, linklocal_prefix); + SWSS_LOG_NOTICE("Removed link local ipv6 route %s to cpu from VRF %s", linklocal_prefix.to_string().c_str(), m_vrfOrch->getVRFname(vrf_id).c_str()); + } + + m_sagMac = MacAddress(); + } +} + bool IntfsOrch::getSaiLoopbackAction(const string &actionStr, sai_packet_action_t &action) { const unordered_map loopbackActionMap = diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index ea15ada14b..7442647bd2 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -25,6 +25,7 @@ struct IntfsEntry int ref_count; sai_object_id_t vrf_id; bool proxy_arp; + MacAddress mac; }; typedef map IntfsTable; @@ -32,7 +33,8 @@ typedef map IntfsTable; class IntfsOrch : public Orch { public: - IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch, DBConnector *chassisAppDb); + IntfsOrch(DBConnector *db, vector tableNames, VRFOrch *vrf_orch, DBConnector *chassisAppDb); + static const int intfsorch_pri; sai_object_id_t getRouterIntfsId(const string&); bool isPrefixSubnet(const IpPrefix&, const string&); @@ -80,8 +82,11 @@ class IntfsOrch : public Orch VRFOrch *m_vrfOrch; IntfsTable m_syncdIntfses; map m_vnetInfses; + MacAddress m_sagMac; + void doTask(Consumer &consumer); void doTask(SelectableTimer &timer); + void doSagTask(std::vector data, const std::string& op); shared_ptr m_counter_db; shared_ptr m_flex_db; diff --git a/orchagent/main.cpp b/orchagent/main.cpp index df702e7c3f..0add517a05 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -505,10 +505,6 @@ int main(int argc, char **argv) attr.value.ptr = (void *)on_switch_shutdown_request; attrs.push_back(attr); - attr.id = SAI_SWITCH_ATTR_PORT_HOST_TX_READY_NOTIFY; - attr.value.ptr = (void *)on_port_host_tx_ready; - attrs.push_back(attr); - if (gMySwitchType != "fabric" && gMacAddress) { attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index efbe7ff481..a2bdebbc62 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -933,6 +933,27 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress } } + PortsOrch* ports_orch = gDirectory.get(); + auto vlan_ports = ports_orch->getAllVlans(); + + for (auto vlan_port: vlan_ports) + { + if (vlan_port == alias) + { + continue; + } + NeighborEntry temp_entry = { ip_address, vlan_port }; + if (m_syncdNeighbors.find(temp_entry) != m_syncdNeighbors.end()) + { + SWSS_LOG_NOTICE("Neighbor %s on %s already exists, removing before adding new neighbor", ip_address.to_string().c_str(), vlan_port.c_str()); + if (!removeNeighbor(temp_entry)) + { + SWSS_LOG_ERROR("Failed to remove neighbor %s on %s", ip_address.to_string().c_str(), vlan_port.c_str()); + return false; + } + } + } + MuxOrch* mux_orch = gDirectory.get(); bool hw_config = isHwConfigured(neighborEntry); @@ -1364,7 +1385,7 @@ void NeighOrch::doVoqSystemNeighTask(Consumer &consumer) continue; } - MacAddress mac_address; + MacAddress mac_address, original_mac_address; uint32_t encap_index = 0; for (auto i = kfvFieldsValues(t).begin(); i != kfvFieldsValues(t).end(); i++) @@ -1444,42 +1465,13 @@ void NeighOrch::doVoqSystemNeighTask(Consumer &consumer) //kernel programming. if(ibif.m_type != Port::VLAN) { + original_mac_address = mac_address; mac_address = gMacAddress; - - // For VS platforms, the mac of the static neigh should not be same as asic's own mac. - // This is because host originated packets will have same mac for both src and dst which - // will result in host NOT sending packet out. To address this problem which is specific - // to port type inband interfaces, set the mac to the neighbor's owner asic's mac. Since - // the owner asic's mac is not readily avaiable here, the owner asic mac is derived from - // the switch id and lower 5 bytes of asic mac which is assumed to be same for all asics - // in the VS system. - // Therefore to make VOQ chassis systems work in VS platform based setups like the setups - // using KVMs, it is required that all asics have same base mac in the format given below - // :<6th byte = switch_id> - string platform = getenv("ASIC_VENDOR") ? getenv("ASIC_VENDOR") : ""; - + // For VS platform, use the original MAC address if (platform == VS_PLATFORM_SUBSTRING) { - int8_t sw_id = -1; - uint8_t egress_asic_mac[ETHER_ADDR_LEN]; - - gMacAddress.getMac(egress_asic_mac); - - if (p.m_type == Port::LAG) - { - sw_id = (int8_t) p.m_system_lag_info.switch_id; - } - else if (p.m_type == Port::PHY || p.m_type == Port::SYSTEM) - { - sw_id = (int8_t) p.m_system_port_info.switch_id; - } - - if(sw_id != -1) - { - egress_asic_mac[5] = sw_id; - mac_address = MacAddress(egress_asic_mac); - } + mac_address = original_mac_address; } } vector fvVector; diff --git a/orchagent/notifications.cpp b/orchagent/notifications.cpp index 442e93d75a..9455620fb5 100644 --- a/orchagent/notifications.cpp +++ b/orchagent/notifications.cpp @@ -27,6 +27,12 @@ void on_bfd_session_state_change(uint32_t count, sai_bfd_session_state_notificat // which causes concurrency access to the DB } +void on_twamp_session_event(uint32_t count, sai_twamp_session_event_notification_data_t *data) +{ + // don't use this event handler, because it runs by libsairedis in a separate thread + // which causes concurrency access to the DB +} + void on_switch_shutdown_request(sai_object_id_t switch_id) { SWSS_LOG_ENTER(); diff --git a/orchagent/notifications.h b/orchagent/notifications.h index 81d49efee0..403b358a12 100644 --- a/orchagent/notifications.h +++ b/orchagent/notifications.h @@ -7,6 +7,7 @@ extern "C" { void on_fdb_event(uint32_t count, sai_fdb_event_notification_data_t *data); void on_port_state_change(uint32_t count, sai_port_oper_status_notification_t *data); void on_bfd_session_state_change(uint32_t count, sai_bfd_session_state_notification_t *data); +void on_twamp_session_event(uint32_t count, sai_twamp_session_event_notification_data_t *data); // The function prototype information can be found here: // https://github.com/sonic-net/sonic-sairedis/blob/master/meta/NotificationSwitchShutdownRequest.cpp#L49 diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index a2e7f86ad9..d0cca8ccb6 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -19,6 +19,8 @@ using namespace swss; #define SELECT_TIMEOUT 1000 #define PFC_WD_POLL_MSECS 100 +#define APP_FABRIC_MONITOR_PORT_TABLE_NAME "FABRIC_PORT_TABLE" + /* orchagent heart beat message interval */ #define HEART_BEAT_INTERVAL_MSECS 10 * 1000 @@ -189,7 +191,12 @@ bool OrchDaemon::init() ChassisOrch* chassis_frontend_orch = new ChassisOrch(m_configDb, m_applDb, chassis_frontend_tables, vnet_rt_orch); gDirectory.set(chassis_frontend_orch); - gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME, vrf_orch, m_chassisAppDb); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + + gIntfsOrch = new IntfsOrch(m_applDb, intf_tables, vrf_orch, m_chassisAppDb); gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassisAppDb); const int fgnhgorch_pri = 15; @@ -513,8 +520,10 @@ bool OrchDaemon::init() if (m_fabricEnabled) { + // register APP_FABRIC_MONITOR_PORT_TABLE_NAME table + const int fabric_portsorch_base_pri = 30; vector fabric_port_tables = { - // empty for now + { APP_FABRIC_MONITOR_PORT_TABLE_NAME, fabric_portsorch_base_pri } }; gFabricPortsOrch = new FabricPortsOrch(m_applDb, fabric_port_tables, m_fabricPortStatEnabled, m_fabricQueueStatEnabled); m_orchList.push_back(gFabricPortsOrch); @@ -727,6 +736,11 @@ bool OrchDaemon::init() gP4Orch = new P4Orch(m_applDb, p4rt_tables, vrf_orch, gCoppOrch); m_orchList.push_back(gP4Orch); + TableConnector confDbTwampTable(m_configDb, CFG_TWAMP_SESSION_TABLE_NAME); + TableConnector stateDbTwampTable(m_stateDb, STATE_TWAMP_SESSION_TABLE_NAME); + TwampOrch *twamp_orch = new TwampOrch(confDbTwampTable, stateDbTwampTable, gSwitchOrch, gPortsOrch, vrf_orch); + m_orchList.push_back(twamp_orch); + if (WarmStart::isWarmStart()) { bool suc = warmRestoreAndSyncUp(); @@ -1067,8 +1081,9 @@ bool FabricOrchDaemon::init() SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("FabricOrchDaemon init"); + const int fabric_portsorch_base_pri = 30; vector fabric_port_tables = { - // empty for now, I don't consume anything yet + { APP_FABRIC_MONITOR_PORT_TABLE_NAME, fabric_portsorch_base_pri } }; gFabricPortsOrch = new FabricPortsOrch(m_applDb, fabric_port_tables); addOrchList(gFabricPortsOrch); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 803a720c3c..2473848bf5 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -46,6 +46,7 @@ #include "bfdorch.h" #include "srv6orch.h" #include "nvgreorch.h" +#include "twamporch.h" #include "dash/dashaclorch.h" #include "dash/dashorch.h" #include "dash/dashrouteorch.h" diff --git a/orchagent/p4orch/acl_rule_manager.cpp b/orchagent/p4orch/acl_rule_manager.cpp index 40f20ba051..5131b718ac 100644 --- a/orchagent/p4orch/acl_rule_manager.cpp +++ b/orchagent/p4orch/acl_rule_manager.cpp @@ -1,5 +1,6 @@ #include "p4orch/acl_rule_manager.h" +#include #include #include #include @@ -9,7 +10,6 @@ #include "crmorch.h" #include "dbconnector.h" #include "intfsorch.h" -#include #include "logger.h" #include "orch.h" #include "p4orch.h" @@ -165,7 +165,8 @@ std::vector getMeterSaiAttrs(const P4AclMeter &p4_acl_meter) } // namespace -ReturnCode AclRuleManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode AclRuleManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { return StatusCode::SWSS_RC_UNIMPLEMENTED; } diff --git a/orchagent/p4orch/acl_rule_manager.h b/orchagent/p4orch/acl_rule_manager.h index 230f226f98..1e65ef7c8d 100644 --- a/orchagent/p4orch/acl_rule_manager.h +++ b/orchagent/p4orch/acl_rule_manager.h @@ -44,7 +44,8 @@ class AclRuleManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; // Update counters stats for every rule in each ACL table in COUNTERS_DB, if // counters are enabled in rules. diff --git a/orchagent/p4orch/acl_table_manager.cpp b/orchagent/p4orch/acl_table_manager.cpp index 416120fa5d..4a3910992e 100644 --- a/orchagent/p4orch/acl_table_manager.cpp +++ b/orchagent/p4orch/acl_table_manager.cpp @@ -1,5 +1,6 @@ #include "p4orch/acl_table_manager.h" +#include #include #include #include @@ -7,7 +8,6 @@ #include "SaiAttributeList.h" #include "crmorch.h" #include "dbconnector.h" -#include #include "logger.h" #include "orch.h" #include "p4orch.h" @@ -205,7 +205,8 @@ ReturnCodeOr> AclTableManager::getUdfSaiAttrs(const return udf_attrs; } -ReturnCode AclTableManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode AclTableManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { return StatusCode::SWSS_RC_UNIMPLEMENTED; } diff --git a/orchagent/p4orch/acl_table_manager.h b/orchagent/p4orch/acl_table_manager.h index 5ebaf459e9..68cc1c9920 100644 --- a/orchagent/p4orch/acl_table_manager.h +++ b/orchagent/p4orch/acl_table_manager.h @@ -34,7 +34,8 @@ class AclTableManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; // Get ACL table definition by table name in cache. Return nullptr if not // found. diff --git a/orchagent/p4orch/acl_util.cpp b/orchagent/p4orch/acl_util.cpp index 0ef81a07e3..5ab2276b4d 100644 --- a/orchagent/p4orch/acl_util.cpp +++ b/orchagent/p4orch/acl_util.cpp @@ -1,7 +1,8 @@ #include "p4orch/acl_util.h" -#include "converter.h" #include + +#include "converter.h" #include "logger.h" #include "sai_serialize.h" #include "table.h" diff --git a/orchagent/p4orch/acl_util.h b/orchagent/p4orch/acl_util.h index 8810843fd6..b4123d0754 100644 --- a/orchagent/p4orch/acl_util.h +++ b/orchagent/p4orch/acl_util.h @@ -1,11 +1,11 @@ #pragma once #include +#include #include #include #include -#include #include "p4orch/p4orch_util.h" #include "return_code.h" extern "C" @@ -243,7 +243,7 @@ struct P4AclTableDefinition P4AclTableDefinition(const std::string &acl_table_name, const sai_acl_stage_t stage, const uint32_t priority, const uint32_t size, const std::string &meter_unit, const std::string &counter_unit) : acl_table_name(acl_table_name), stage(stage), priority(priority), size(size), meter_unit(meter_unit), - counter_unit(counter_unit){}; + counter_unit(counter_unit) {}; }; struct P4UserDefinedTrapHostifTableEntry @@ -251,7 +251,7 @@ struct P4UserDefinedTrapHostifTableEntry sai_object_id_t user_defined_trap; sai_object_id_t hostif_table_entry; P4UserDefinedTrapHostifTableEntry() - : user_defined_trap(SAI_NULL_OBJECT_ID), hostif_table_entry(SAI_NULL_OBJECT_ID){}; + : user_defined_trap(SAI_NULL_OBJECT_ID), hostif_table_entry(SAI_NULL_OBJECT_ID) {}; }; using acl_rule_attr_lookup_t = std::map; diff --git a/orchagent/p4orch/ext_tables_manager.cpp b/orchagent/p4orch/ext_tables_manager.cpp index 8b5ded6d7f..ae091fcd77 100644 --- a/orchagent/p4orch/ext_tables_manager.cpp +++ b/orchagent/p4orch/ext_tables_manager.cpp @@ -1,22 +1,22 @@ #include "p4orch/ext_tables_manager.h" +#include #include +#include #include #include #include #include -#include +#include "crmorch.h" #include "directory.h" -#include #include "logger.h" -#include "tokenize.h" #include "orch.h" -#include "crmorch.h" #include "p4orch/p4orch.h" #include "p4orch/p4orch_util.h" +#include "tokenize.h" -extern sai_counter_api_t* sai_counter_api; +extern sai_counter_api_t *sai_counter_api; extern sai_generic_programmable_api_t *sai_generic_programmable_api; extern Directory gDirectory; @@ -43,10 +43,10 @@ std::string getCrossRefTableName(const std::string table_name) auto it = FixedTablesMap.find(table_name); if (it != FixedTablesMap.end()) { - return(it->second); + return (it->second); } - return(table_name); + return (table_name); } ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry &app_db_entry, ActionInfo *action) @@ -55,8 +55,7 @@ ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry & std::unordered_map cross_ref_key_j; ReturnCode status; - for (auto param_defn_it = action->params.begin(); - param_defn_it != action->params.end(); param_defn_it++) + for (auto param_defn_it = action->params.begin(); param_defn_it != action->params.end(); param_defn_it++) { ActionParamInfo action_param_defn = param_defn_it->second; if (action_param_defn.table_reference_map.empty()) @@ -71,24 +70,24 @@ ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry & { SWSS_LOG_ERROR("Required param not specified for action %s\n", action_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Required param not specified for action %s " << action_name.c_str(); + << "Required param not specified for action %s " << action_name.c_str(); } for (auto cross_ref_it = action_param_defn.table_reference_map.begin(); - cross_ref_it != action_param_defn.table_reference_map.end(); cross_ref_it++) + cross_ref_it != action_param_defn.table_reference_map.end(); cross_ref_it++) { - cross_ref_key_j[cross_ref_it->first].push_back(nlohmann::json::object_t::value_type(prependMatchField(cross_ref_it->second), app_db_param_it->second)); + cross_ref_key_j[cross_ref_it->first].push_back( + nlohmann::json::object_t::value_type(prependMatchField(cross_ref_it->second), app_db_param_it->second)); } } - for (auto it = cross_ref_key_j.begin(); it != cross_ref_key_j.end(); it++) { const std::string table_name = getCrossRefTableName(it->first); const std::string table_key = it->second.dump(); std::string key; sai_object_type_t object_type; - sai_object_id_t oid; + sai_object_id_t oid; DepObject dep_object = {}; if (gP4Orch->m_p4TableToManagerMap.find(table_name) != gP4Orch->m_p4TableToManagerMap.end()) @@ -98,10 +97,10 @@ ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry & { SWSS_LOG_ERROR("Cross-table reference validation failed from fixed-table %s", table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Cross-table reference valdiation failed from fixed-table"; + << "Cross-table reference valdiation failed from fixed-table"; } } - else + else { if (getTableInfo(table_name)) { @@ -109,16 +108,21 @@ ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry & status = getSaiObject(ext_table_key, object_type, key); if (!status.ok()) { - SWSS_LOG_ERROR("Cross-table reference validation failed from extension-table %s", table_name.c_str()); + SWSS_LOG_ERROR("Cross-table reference validation failed from extension-table %s", + table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Cross-table reference valdiation failed from extension table"; + << "Cross-table reference valdiation failed from extension " + "table"; } } else { - SWSS_LOG_ERROR("Cross-table reference validation failed due to non-existent table %s", table_name.c_str()); + SWSS_LOG_ERROR("Cross-table reference validation failed due to non-existent table " + "%s", + table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Cross-table reference valdiation failed due to non-existent table"; + << "Cross-table reference valdiation failed due to non-existent " + "table"; } } @@ -126,19 +130,20 @@ ReturnCode ExtTablesManager::validateActionParamsCrossRef(P4ExtTableAppDbEntry & { SWSS_LOG_ERROR("Cross-table reference validation failed, no OID found from table %s", table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Cross-table reference valdiation failed, no OID found"; + << "Cross-table reference valdiation failed, no OID found"; } if (oid == SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("Cross-table reference validation failed, null OID expected from table %s", table_name.c_str()); - return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Cross-table reference valdiation failed, null OID"; + SWSS_LOG_ERROR("Cross-table reference validation failed, null OID expected from " + "table %s", + table_name.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Cross-table reference valdiation failed, null OID"; } - dep_object.sai_object = object_type; - dep_object.key = key; - dep_object.oid = oid; + dep_object.sai_object = object_type; + dep_object.key = key; + dep_object.oid = oid; app_db_entry.action_dep_objects[action_name] = dep_object; } @@ -157,7 +162,7 @@ ReturnCode ExtTablesManager::validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry & { SWSS_LOG_ERROR("Not a valid extension table %s", app_db_entry.table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Not a valid extension table " << app_db_entry.table_name.c_str(); + << "Not a valid extension table " << app_db_entry.table_name.c_str(); } if (table->action_ref_tables.empty()) @@ -167,15 +172,15 @@ ReturnCode ExtTablesManager::validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry & ActionInfo *action; for (auto app_db_action_it = app_db_entry.action_params.begin(); - app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) + app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) { auto action_name = app_db_action_it->first; action = getTableActionInfo(table, action_name); if (action == nullptr) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Not a valid action " << action_name.c_str() - << " in extension table " << app_db_entry.table_name.c_str(); + << "Not a valid action " << action_name.c_str() << " in extension table " + << app_db_entry.table_name.c_str(); } if (!action->refers_to) @@ -186,25 +191,23 @@ ReturnCode ExtTablesManager::validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry & status = validateActionParamsCrossRef(app_db_entry, action); if (!status.ok()) { - return status; + return status; } } return ReturnCode(); } - ReturnCodeOr ExtTablesManager::deserializeP4ExtTableEntry( - const std::string &table_name, - const std::string &key, const std::vector &attributes) + const std::string &table_name, const std::string &key, const std::vector &attributes) { - std::string action_name; + std::string action_name; SWSS_LOG_ENTER(); P4ExtTableAppDbEntry app_db_entry_or = {}; app_db_entry_or.table_name = table_name; - app_db_entry_or.table_key = key; + app_db_entry_or.table_key = key; action_name = ""; for (const auto &it : attributes) @@ -223,7 +226,7 @@ ReturnCodeOr ExtTablesManager::deserializeP4ExtTableEntry( { SWSS_LOG_ERROR("Unknown extension entry field"); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Unknown extension entry field " << QuotedVar(field); + << "Unknown extension entry field " << QuotedVar(field); } const auto &prefix = tokenized_fields[0]; @@ -244,11 +247,10 @@ ReturnCodeOr ExtTablesManager::deserializeP4ExtTableEntry( return app_db_entry_or; } - ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry &app_db_entry, std::string &ext_table_entry_attr) { - nlohmann::json sai_j, sai_metadata_j, sai_array_j = {}, sai_entry_j; + nlohmann::json sai_j, sai_metadata_j, sai_array_j = {}, sai_entry_j; SWSS_LOG_ENTER(); @@ -260,37 +262,37 @@ ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry { SWSS_LOG_ERROR("extension entry for invalid table %s", app_db_entry.table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "extension entry for invalid table " << app_db_entry.table_name.c_str(); + << "extension entry for invalid table " << app_db_entry.table_name.c_str(); } nlohmann::json j = nlohmann::json::parse(app_db_entry.table_key); for (auto it = j.begin(); it != j.end(); ++it) { - std::string match, value, prefix; - std::size_t pos; + std::string match, value, prefix; + std::size_t pos; match = it.key(); value = it.value(); - prefix = p4orch::kMatchPrefix; + prefix = p4orch::kMatchPrefix; pos = match.rfind(prefix); if (pos != std::string::npos) { match.erase(0, prefix.length()); } - else + else { SWSS_LOG_ERROR("Failed to encode match fields for sai call"); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to encode match fields for sai call"; } - prefix = p4orch::kFieldDelimiter; + prefix = p4orch::kFieldDelimiter; pos = match.rfind(prefix); if (pos != std::string::npos) { match.erase(0, prefix.length()); } - else + else { SWSS_LOG_ERROR("Failed to encode match fields for sai call"); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "Failed to encode match fields for sai call"; @@ -301,7 +303,7 @@ ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry { SWSS_LOG_ERROR("extension entry for invalid match field %s", match.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "extension entry for invalid match field " << match.c_str(); + << "extension entry for invalid match field " << match.c_str(); } sai_metadata_j = nlohmann::json::object({}); @@ -315,7 +317,7 @@ ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry } for (auto app_db_action_it = app_db_entry.action_params.begin(); - app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) + app_db_action_it != app_db_entry.action_params.end(); app_db_action_it++) { sai_j = nlohmann::json::object({}); auto action_dep_object_it = app_db_entry.action_dep_objects.find(app_db_action_it->first); @@ -323,9 +325,9 @@ ReturnCode ExtTablesManager::prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry { auto action_defn_it = table->action_fields.find(app_db_action_it->first); for (auto app_db_param_it = app_db_action_it->second.begin(); - app_db_param_it != app_db_action_it->second.end(); app_db_param_it++) + app_db_param_it != app_db_action_it->second.end(); app_db_param_it++) { - nlohmann::json params_j = nlohmann::json::object({}); + nlohmann::json params_j = nlohmann::json::object({}); if (action_defn_it != table->action_fields.end()) { auto param_defn_it = action_defn_it->second.params.find(app_db_param_it->first); @@ -396,9 +398,8 @@ bool createGenericCounter(sai_object_id_t &counter_id) return true; } - ReturnCode ExtTablesManager::createP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, - P4ExtTableEntry &ext_table_entry) + P4ExtTableEntry &ext_table_entry) { ReturnCode status; sai_object_type_t object_type; @@ -428,22 +429,20 @@ ReturnCode ExtTablesManager::createP4ExtTableEntry(const P4ExtTableAppDbEntry &a generic_programmable_attr.value.json.json.list = (int8_t *)const_cast(ext_table_entry_attr.c_str()); generic_programmable_attrs.push_back(generic_programmable_attr); - auto *table = getTableInfo(app_db_entry.table_name); if (!table) { SWSS_LOG_ERROR("extension entry for invalid table %s", app_db_entry.table_name.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "extension entry for invalid table " << app_db_entry.table_name.c_str(); + << "extension entry for invalid table " << app_db_entry.table_name.c_str(); } if (table->counter_bytes_enabled || table->counter_packets_enabled) { if (!createGenericCounter(counter_id)) { - SWSS_LOG_WARN("Failed to create counter for table %s, key %s\n", - app_db_entry.table_name.c_str(), - app_db_entry.table_key.c_str()); + SWSS_LOG_WARN("Failed to create counter for table %s, key %s\n", app_db_entry.table_name.c_str(), + app_db_entry.table_key.c_str()); } else { @@ -457,33 +456,29 @@ ReturnCode ExtTablesManager::createP4ExtTableEntry(const P4ExtTableAppDbEntry &a sai_object_id_t sai_generic_programmable_oid = SAI_NULL_OBJECT_ID; sai_status_t sai_status = sai_generic_programmable_api->create_generic_programmable( - &sai_generic_programmable_oid, gSwitchId, - (uint32_t)generic_programmable_attrs.size(), - generic_programmable_attrs.data()); + &sai_generic_programmable_oid, gSwitchId, (uint32_t)generic_programmable_attrs.size(), + generic_programmable_attrs.data()); if (sai_status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("create sai api call failed for extension entry table %s, entry %s", - app_db_entry.table_name.c_str(), app_db_entry.table_key.c_str()); + app_db_entry.table_name.c_str(), app_db_entry.table_key.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "create sai api call failed for extension entry table " - << app_db_entry.table_name.c_str() - << " , entry " << app_db_entry.table_key.c_str(); + << "create sai api call failed for extension entry table " << app_db_entry.table_name.c_str() + << " , entry " << app_db_entry.table_key.c_str(); } std::string crm_table_name = "EXT_" + app_db_entry.table_name; boost::algorithm::to_upper(crm_table_name); gCrmOrch->incCrmExtTableUsedCounter(CrmResourceType::CRM_EXT_TABLE, crm_table_name); - ext_table_entry.sai_entry_oid = sai_generic_programmable_oid; for (auto action_dep_object_it = app_db_entry.action_dep_objects.begin(); - action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) + action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) { auto action_dep_object = action_dep_object_it->second; m_p4OidMapper->increaseRefCount(action_dep_object.sai_object, action_dep_object.key); ext_table_entry.action_dep_objects[action_dep_object_it->first] = action_dep_object; } - auto ext_table_key = KeyGenerator::generateExtTableKey(app_db_entry.table_name, app_db_entry.table_key); status = getSaiObject(ext_table_key, object_type, key); if (!status.ok()) @@ -497,9 +492,8 @@ ReturnCode ExtTablesManager::createP4ExtTableEntry(const P4ExtTableAppDbEntry &a return ReturnCode(); } - ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, - P4ExtTableEntry *ext_table_entry) + P4ExtTableEntry *ext_table_entry) { ReturnCode status; std::string ext_table_entry_attr; @@ -510,11 +504,10 @@ ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &a if (ext_table_entry->sai_entry_oid == SAI_NULL_OBJECT_ID) { SWSS_LOG_ERROR("update sai api call for NULL extension entry table %s, entry %s", - app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); + app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "update sai api call for NULL extension entry table " - << app_db_entry.table_name.c_str() - << " , entry " << ext_table_entry->table_key.c_str(); + << "update sai api call for NULL extension entry table " << app_db_entry.table_name.c_str() + << " , entry " << ext_table_entry->table_key.c_str(); } status = prepareP4SaiExtAPIParams(app_db_entry, ext_table_entry_attr); @@ -531,24 +524,21 @@ ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &a generic_programmable_attr.value.json.json.list = (int8_t *)const_cast(ext_table_entry_attr.c_str()); sai_status_t sai_status = sai_generic_programmable_api->set_generic_programmable_attribute( - ext_table_entry->sai_entry_oid, - &generic_programmable_attr); + ext_table_entry->sai_entry_oid, &generic_programmable_attr); if (sai_status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("update sai api call failed for extension entry table %s, entry %s", - app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); + app_db_entry.table_name.c_str(), ext_table_entry->table_key.c_str()); return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "update sai api call failed for extension entry table " - << app_db_entry.table_name.c_str() - << " , entry " << ext_table_entry->table_key.c_str(); + << "update sai api call failed for extension entry table " << app_db_entry.table_name.c_str() + << " , entry " << ext_table_entry->table_key.c_str(); } - old_action_dep_objects = ext_table_entry->action_dep_objects; ext_table_entry->action_dep_objects.clear(); for (auto action_dep_object_it = app_db_entry.action_dep_objects.begin(); - action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) + action_dep_object_it != app_db_entry.action_dep_objects.end(); action_dep_object_it++) { auto action_dep_object = action_dep_object_it->second; m_p4OidMapper->increaseRefCount(action_dep_object.sai_object, action_dep_object.key); @@ -556,7 +546,7 @@ ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &a } for (auto old_action_dep_object_it = old_action_dep_objects.begin(); - old_action_dep_object_it != old_action_dep_objects.end(); old_action_dep_object_it++) + old_action_dep_object_it != old_action_dep_objects.end(); old_action_dep_object_it++) { auto old_action_dep_object = old_action_dep_object_it->second; m_p4OidMapper->decreaseRefCount(old_action_dep_object.sai_object, old_action_dep_object.key); @@ -565,8 +555,7 @@ ReturnCode ExtTablesManager::updateP4ExtTableEntry(const P4ExtTableAppDbEntry &a return ReturnCode(); } -ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name, - const std::string &table_key) +ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name, const std::string &table_key) { ReturnCode status; sai_object_type_t object_type; @@ -578,36 +567,31 @@ ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name if (!ext_table_entry) { LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_NOT_FOUND) - << "extension entry with key " << QuotedVar(table_key) - << " does not exist for table " << QuotedVar(table_name)); + << "extension entry with key " << QuotedVar(table_key) << " does not exist for table " + << QuotedVar(table_name)); } if (ext_table_entry->sai_entry_oid == SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("remove sai api call for NULL extension entry table %s, entry %s", - table_name.c_str(), table_key.c_str()); - return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "remove sai api call for NULL extension entry table " - << table_name.c_str() << " , entry " << table_key.c_str(); + SWSS_LOG_ERROR("remove sai api call for NULL extension entry table %s, entry %s", table_name.c_str(), + table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "remove sai api call for NULL extension entry table " + << table_name.c_str() << " , entry " << table_key.c_str(); } - SWSS_LOG_ERROR("table: %s, key: %s", ext_table_entry->table_name.c_str(), - ext_table_entry->table_key.c_str()); - sai_status_t sai_status = sai_generic_programmable_api->remove_generic_programmable( - ext_table_entry->sai_entry_oid); + SWSS_LOG_ERROR("table: %s, key: %s", ext_table_entry->table_name.c_str(), ext_table_entry->table_key.c_str()); + sai_status_t sai_status = sai_generic_programmable_api->remove_generic_programmable(ext_table_entry->sai_entry_oid); if (sai_status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("remove sai api call failed for extension entry table %s, entry %s", - table_name.c_str(), table_key.c_str()); - return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "remove sai api call failed for extension entry table " - << table_name.c_str() << " , entry " << table_key.c_str(); + SWSS_LOG_ERROR("remove sai api call failed for extension entry table %s, entry %s", table_name.c_str(), + table_key.c_str()); + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "remove sai api call failed for extension entry table " + << table_name.c_str() << " , entry " << table_key.c_str(); } std::string crm_table_name = "EXT_" + table_name; boost::algorithm::to_upper(crm_table_name); gCrmOrch->decCrmExtTableUsedCounter(CrmResourceType::CRM_EXT_TABLE, crm_table_name); - auto ext_table_key = KeyGenerator::generateExtTableKey(table_name, table_key); status = getSaiObject(ext_table_key, object_type, key); if (!status.ok()) @@ -630,7 +614,7 @@ ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name m_p4OidMapper->eraseOID(object_type, key); for (auto action_dep_object_it = ext_table_entry->action_dep_objects.begin(); - action_dep_object_it != ext_table_entry->action_dep_objects.end(); action_dep_object_it++) + action_dep_object_it != ext_table_entry->action_dep_objects.end(); action_dep_object_it++) { auto action_dep_object = action_dep_object_it->second; m_p4OidMapper->decreaseRefCount(action_dep_object.sai_object, action_dep_object.key); @@ -647,7 +631,6 @@ ReturnCode ExtTablesManager::removeP4ExtTableEntry(const std::string &table_name return ReturnCode(); } - ReturnCode ExtTablesManager::processAddRequest(const P4ExtTableAppDbEntry &app_db_entry) { SWSS_LOG_ENTER(); @@ -662,15 +645,14 @@ ReturnCode ExtTablesManager::processAddRequest(const P4ExtTableAppDbEntry &app_d } ReturnCode ExtTablesManager::processUpdateRequest(const P4ExtTableAppDbEntry &app_db_entry, - P4ExtTableEntry *ext_table_entry) + P4ExtTableEntry *ext_table_entry) { SWSS_LOG_ENTER(); auto status = updateP4ExtTableEntry(app_db_entry, ext_table_entry); if (!status.ok()) { - SWSS_LOG_ERROR("Failed to update extension entry with key %s", - app_db_entry.table_key.c_str()); + SWSS_LOG_ERROR("Failed to update extension entry with key %s", app_db_entry.table_key.c_str()); } return ReturnCode(); } @@ -682,14 +664,13 @@ ReturnCode ExtTablesManager::processDeleteRequest(const P4ExtTableAppDbEntry &ap auto status = removeP4ExtTableEntry(app_db_entry.table_name, app_db_entry.table_key); if (!status.ok()) { - SWSS_LOG_ERROR("Failed to remove extension entry with key %s", - app_db_entry.table_key.c_str()); + SWSS_LOG_ERROR("Failed to remove extension entry with key %s", app_db_entry.table_key.c_str()); } return ReturnCode(); } - -ReturnCode ExtTablesManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode ExtTablesManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { object_type = SAI_OBJECT_TYPE_GENERIC_PROGRAMMABLE; object_key = json_key; @@ -707,97 +688,102 @@ void ExtTablesManager::drain() SWSS_LOG_ENTER(); std::string table_prefix = "EXT_"; - if (gP4Orch->tablesinfo) { - for (auto table_it = gP4Orch->tablesinfo->m_tablePrecedenceMap.begin(); - table_it != gP4Orch->tablesinfo->m_tablePrecedenceMap.end(); ++table_it) - { - auto table_name = table_prefix + table_it->second; - boost::algorithm::to_upper(table_name); - auto it_m = m_entriesTables.find(table_name); - if (it_m == m_entriesTables.end()) - { - continue; - } - - for (const auto &key_op_fvs_tuple : it_m->second) + if (gP4Orch->tablesinfo) + { + for (auto table_it = gP4Orch->tablesinfo->m_tablePrecedenceMap.begin(); + table_it != gP4Orch->tablesinfo->m_tablePrecedenceMap.end(); ++table_it) { - std::string table_name; - std::string table_key; - - parseP4RTKey(kfvKey(key_op_fvs_tuple), &table_name, &table_key); - const std::vector &attributes = kfvFieldsValues(key_op_fvs_tuple); - - if (table_name.rfind(table_prefix, 0) == std::string::npos) + auto table_name = table_prefix + table_it->second; + boost::algorithm::to_upper(table_name); + auto it_m = m_entriesTables.find(table_name); + if (it_m == m_entriesTables.end()) { - SWSS_LOG_ERROR("Table %s is without prefix %s", table_name.c_str(), table_prefix.c_str()); - m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - StatusCode::SWSS_RC_INVALID_PARAM, /*replace=*/true); continue; } - table_name = table_name.substr(table_prefix.length()); - boost::algorithm::to_lower(table_name); - ReturnCode status; - auto app_db_entry_or = deserializeP4ExtTableEntry(table_name, table_key, attributes); - if (!app_db_entry_or.ok()) + for (const auto &key_op_fvs_tuple : it_m->second) { - status = app_db_entry_or.status(); - SWSS_LOG_ERROR("Unable to deserialize APP DB entry with key %s: %s", - QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); - m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); - continue; - } + std::string table_name; + std::string table_key; - auto &app_db_entry = *app_db_entry_or; - status = validateP4ExtTableAppDbEntry(app_db_entry); - if (!status.ok()) - { - SWSS_LOG_ERROR("Validation failed for extension APP DB entry with key %s: %s", - QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); - m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); - continue; - } + parseP4RTKey(kfvKey(key_op_fvs_tuple), &table_name, &table_key); + const std::vector &attributes = kfvFieldsValues(key_op_fvs_tuple); - const std::string &operation = kfvOp(key_op_fvs_tuple); - if (operation == SET_COMMAND) - { - auto *ext_table_entry = getP4ExtTableEntry(app_db_entry.table_name, app_db_entry.table_key); - if (ext_table_entry == nullptr) + if (table_name.rfind(table_prefix, 0) == std::string::npos) + { + SWSS_LOG_ERROR("Table %s is without prefix %s", table_name.c_str(), table_prefix.c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), + kfvFieldsValues(key_op_fvs_tuple), StatusCode::SWSS_RC_INVALID_PARAM, + /*replace=*/true); + continue; + } + table_name = table_name.substr(table_prefix.length()); + boost::algorithm::to_lower(table_name); + + ReturnCode status; + auto app_db_entry_or = deserializeP4ExtTableEntry(table_name, table_key, attributes); + if (!app_db_entry_or.ok()) + { + status = app_db_entry_or.status(); + SWSS_LOG_ERROR("Unable to deserialize APP DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), + kfvFieldsValues(key_op_fvs_tuple), status, + /*replace=*/true); + continue; + } + + auto &app_db_entry = *app_db_entry_or; + status = validateP4ExtTableAppDbEntry(app_db_entry); + if (!status.ok()) + { + SWSS_LOG_ERROR("Validation failed for extension APP DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), + kfvFieldsValues(key_op_fvs_tuple), status, + /*replace=*/true); + continue; + } + + const std::string &operation = kfvOp(key_op_fvs_tuple); + if (operation == SET_COMMAND) { - // Create extension entry - app_db_entry.db_key = kfvKey(key_op_fvs_tuple); - status = processAddRequest(app_db_entry); + auto *ext_table_entry = getP4ExtTableEntry(app_db_entry.table_name, app_db_entry.table_key); + if (ext_table_entry == nullptr) + { + // Create extension entry + app_db_entry.db_key = kfvKey(key_op_fvs_tuple); + status = processAddRequest(app_db_entry); + } + else + { + // Modify existing extension entry + status = processUpdateRequest(app_db_entry, ext_table_entry); + } + } + else if (operation == DEL_COMMAND) + { + // Delete extension entry + status = processDeleteRequest(app_db_entry); } else { - // Modify existing extension entry - status = processUpdateRequest(app_db_entry, ext_table_entry); + status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) + << "Unknown operation type " << QuotedVar(operation); + SWSS_LOG_ERROR("%s", status.message().c_str()); } + if (!status.ok()) + { + SWSS_LOG_ERROR("Processing failed for extension APP_DB entry with key %s: %s", + QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); + } + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), + status, + /*replace=*/true); } - else if (operation == DEL_COMMAND) - { - // Delete extension entry - status = processDeleteRequest(app_db_entry); - } - else - { - status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Unknown operation type " << QuotedVar(operation); - SWSS_LOG_ERROR("%s", status.message().c_str()); - } - if (!status.ok()) - { - SWSS_LOG_ERROR("Processing failed for extension APP_DB entry with key %s: %s", - QuotedVar(kfvKey(key_op_fvs_tuple)).c_str(), status.message().c_str()); - } - m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); - } - it_m->second.clear(); - } + it_m->second.clear(); + } } // Now report error for all remaining un-processed entries @@ -813,7 +799,6 @@ void ExtTablesManager::drain() } } - void ExtTablesManager::doExtCounterStatsTask() { SWSS_LOG_ENTER(); @@ -823,12 +808,12 @@ void ExtTablesManager::doExtCounterStatsTask() return; } - sai_stat_id_t stat_ids[] = { SAI_COUNTER_STAT_PACKETS, SAI_COUNTER_STAT_BYTES }; + sai_stat_id_t stat_ids[] = {SAI_COUNTER_STAT_PACKETS, SAI_COUNTER_STAT_BYTES}; uint64_t stats[2]; std::vector counter_stats_values; for (auto table_it = gP4Orch->tablesinfo->m_tableInfoMap.begin(); - table_it != gP4Orch->tablesinfo->m_tableInfoMap.end(); ++table_it) + table_it != gP4Orch->tablesinfo->m_tableInfoMap.end(); ++table_it) { if (!table_it->second.counter_bytes_enabled && !table_it->second.counter_packets_enabled) { @@ -842,8 +827,8 @@ void ExtTablesManager::doExtCounterStatsTask() continue; } - for (auto ext_table_entry_it = ext_table_it->second.begin(); - ext_table_entry_it != ext_table_it->second.end(); ++ext_table_entry_it) + for (auto ext_table_entry_it = ext_table_it->second.begin(); ext_table_entry_it != ext_table_it->second.end(); + ++ext_table_entry_it) { auto *ext_table_entry = &ext_table_entry_it->second; if (ext_table_entry->sai_counter_oid == SAI_NULL_OBJECT_ID) @@ -852,18 +837,17 @@ void ExtTablesManager::doExtCounterStatsTask() } sai_status_t sai_status = - sai_counter_api->get_counter_stats(ext_table_entry->sai_counter_oid, 2, stat_ids, stats); + sai_counter_api->get_counter_stats(ext_table_entry->sai_counter_oid, 2, stat_ids, stats); if (sai_status != SAI_STATUS_SUCCESS) { - SWSS_LOG_WARN("Failed to set counters stats for extension entry %s:%s in COUNTERS_DB: ", - table_name.c_str(), ext_table_entry->table_key.c_str()); + SWSS_LOG_WARN("Failed to set counters stats for extension entry %s:%s in " + "COUNTERS_DB: ", + table_name.c_str(), ext_table_entry->table_key.c_str()); continue; } - counter_stats_values.push_back( - swss::FieldValueTuple{P4_COUNTER_STATS_PACKETS, std::to_string(stats[0])}); - counter_stats_values.push_back( - swss::FieldValueTuple{P4_COUNTER_STATS_BYTES, std::to_string(stats[1])}); + counter_stats_values.push_back(swss::FieldValueTuple{P4_COUNTER_STATS_PACKETS, std::to_string(stats[0])}); + counter_stats_values.push_back(swss::FieldValueTuple{P4_COUNTER_STATS_BYTES, std::to_string(stats[1])}); // Set field value tuples for counters stats in COUNTERS_DB m_countersTable->set(ext_table_entry->db_key, counter_stats_values); @@ -878,4 +862,3 @@ std::string ExtTablesManager::verifyState(const std::string &key, const std::vec return result; } - diff --git a/orchagent/p4orch/ext_tables_manager.h b/orchagent/p4orch/ext_tables_manager.h index cb61d5f308..82256f72ba 100644 --- a/orchagent/p4orch/ext_tables_manager.h +++ b/orchagent/p4orch/ext_tables_manager.h @@ -1,12 +1,12 @@ #pragma once #include +#include #include #include #include #include "macaddress.h" -#include #include "orch.h" #include "p4orch/object_manager_interface.h" #include "p4orch/p4oidmapper.h" @@ -31,7 +31,7 @@ struct P4ExtTableEntry P4ExtTableEntry() {}; P4ExtTableEntry(const std::string &db_key, const std::string &table_name, const std::string &table_key) - : db_key(db_key), table_name(table_name), table_key(table_key) + : db_key(db_key), table_name(table_name), table_key(table_key) { } }; @@ -44,10 +44,9 @@ class ExtTablesManager : public ObjectManagerInterface { public: ExtTablesManager(P4OidMapper *p4oidMapper, VRFOrch *vrfOrch, ResponsePublisherInterface *publisher) - : m_vrfOrch(vrfOrch), - m_countersDb(std::make_unique("COUNTERS_DB", 0)), - m_countersTable(std::make_unique( - m_countersDb.get(), std::string(COUNTERS_TABLE) + DEFAULT_KEY_SEPARATOR + APP_P4RT_TABLE_NAME)) + : m_vrfOrch(vrfOrch), m_countersDb(std::make_unique("COUNTERS_DB", 0)), + m_countersTable(std::make_unique( + m_countersDb.get(), std::string(COUNTERS_TABLE) + DEFAULT_KEY_SEPARATOR + APP_P4RT_TABLE_NAME)) { SWSS_LOG_ENTER(); @@ -61,21 +60,20 @@ class ExtTablesManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; // For every extension entry, update counters stats in COUNTERS_DB, if // counters are enabled for those entries void doExtCounterStatsTask(); private: - ReturnCodeOr deserializeP4ExtTableEntry( - const std::string &table_name, - const std::string &key, const std::vector &attributes); + ReturnCodeOr deserializeP4ExtTableEntry(const std::string &table_name, const std::string &key, + const std::vector &attributes); ReturnCode validateActionParamsCrossRef(P4ExtTableAppDbEntry &app_db_entry, ActionInfo *action); ReturnCode validateP4ExtTableAppDbEntry(P4ExtTableAppDbEntry &app_db_entry); P4ExtTableEntry *getP4ExtTableEntry(const std::string &table_name, const std::string &table_key); - ReturnCode prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry &app_db_entry, - std::string &ext_table_entry_attr); + ReturnCode prepareP4SaiExtAPIParams(const P4ExtTableAppDbEntry &app_db_entry, std::string &ext_table_entry_attr); ReturnCode createP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, P4ExtTableEntry &ext_table_entry); ReturnCode updateP4ExtTableEntry(const P4ExtTableAppDbEntry &app_db_entry, P4ExtTableEntry *ext_table_entry); ReturnCode removeP4ExtTableEntry(const std::string &table_name, const std::string &table_key); diff --git a/orchagent/p4orch/gre_tunnel_manager.cpp b/orchagent/p4orch/gre_tunnel_manager.cpp index 9f4cc7f7b6..c3bfd7d6d7 100644 --- a/orchagent/p4orch/gre_tunnel_manager.cpp +++ b/orchagent/p4orch/gre_tunnel_manager.cpp @@ -1,6 +1,7 @@ #include "p4orch/gre_tunnel_manager.h" #include +#include #include #include #include @@ -9,7 +10,6 @@ #include "crmorch.h" #include "dbconnector.h" #include "ipaddress.h" -#include #include "logger.h" #include "p4orch/p4orch_util.h" #include "sai_serialize.h" @@ -98,7 +98,8 @@ P4GreTunnelEntry::P4GreTunnelEntry(const std::string &tunnel_id, const std::stri tunnel_key = KeyGenerator::generateTunnelKey(tunnel_id); } -ReturnCode GreTunnelManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode GreTunnelManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { return StatusCode::SWSS_RC_UNIMPLEMENTED; } diff --git a/orchagent/p4orch/gre_tunnel_manager.h b/orchagent/p4orch/gre_tunnel_manager.h index d5cb32e9bf..2eee9b18c4 100644 --- a/orchagent/p4orch/gre_tunnel_manager.h +++ b/orchagent/p4orch/gre_tunnel_manager.h @@ -72,7 +72,8 @@ class GreTunnelManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; ReturnCodeOr getConstGreTunnelEntry(const std::string &gre_tunnel_key); diff --git a/orchagent/p4orch/l3_admit_manager.cpp b/orchagent/p4orch/l3_admit_manager.cpp index d319c54a73..da5b955dba 100644 --- a/orchagent/p4orch/l3_admit_manager.cpp +++ b/orchagent/p4orch/l3_admit_manager.cpp @@ -1,13 +1,13 @@ #include "p4orch/l3_admit_manager.h" #include +#include #include #include #include #include "SaiAttributeList.h" #include "dbconnector.h" -#include #include "logger.h" #include "p4orch/p4orch_util.h" #include "portsorch.h" @@ -64,7 +64,8 @@ ReturnCodeOr> getSaiAttrs(const P4L3AdmitEntry &l3_ } // namespace -ReturnCode L3AdmitManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode L3AdmitManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { return StatusCode::SWSS_RC_UNIMPLEMENTED; } diff --git a/orchagent/p4orch/l3_admit_manager.h b/orchagent/p4orch/l3_admit_manager.h index d378775c4f..5f0af69b71 100644 --- a/orchagent/p4orch/l3_admit_manager.h +++ b/orchagent/p4orch/l3_admit_manager.h @@ -63,7 +63,8 @@ class L3AdmitManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: // Gets the internal cached next hop entry by its key. diff --git a/orchagent/p4orch/mirror_session_manager.cpp b/orchagent/p4orch/mirror_session_manager.cpp index 3554344fb3..e562b87ff5 100644 --- a/orchagent/p4orch/mirror_session_manager.cpp +++ b/orchagent/p4orch/mirror_session_manager.cpp @@ -1,10 +1,10 @@ #include "p4orch/mirror_session_manager.h" #include +#include #include "SaiAttributeList.h" #include "dbconnector.h" -#include #include "p4orch/p4orch_util.h" #include "portsorch.h" #include "sai_serialize.h" @@ -21,13 +21,14 @@ extern sai_object_id_t gSwitchId; namespace p4orch { -ReturnCode MirrorSessionManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode MirrorSessionManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { - std::string value; + std::string value; try { - nlohmann::json j = nlohmann::json::parse(json_key); + nlohmann::json j = nlohmann::json::parse(json_key); if (j.find(prependMatchField(p4orch::kMirrorSessionId)) != j.end()) { value = j.at(prependMatchField(p4orch::kMirrorSessionId)).get(); diff --git a/orchagent/p4orch/mirror_session_manager.h b/orchagent/p4orch/mirror_session_manager.h index 5f1c26e10a..7c2bf3b3b1 100644 --- a/orchagent/p4orch/mirror_session_manager.h +++ b/orchagent/p4orch/mirror_session_manager.h @@ -87,7 +87,8 @@ class MirrorSessionManager : public ObjectManagerInterface std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: ReturnCodeOr deserializeP4MirrorSessionAppDbEntry( diff --git a/orchagent/p4orch/neighbor_manager.cpp b/orchagent/p4orch/neighbor_manager.cpp index 7eab967183..f68f22a545 100644 --- a/orchagent/p4orch/neighbor_manager.cpp +++ b/orchagent/p4orch/neighbor_manager.cpp @@ -1,5 +1,6 @@ #include "p4orch/neighbor_manager.h" +#include #include #include #include @@ -7,7 +8,6 @@ #include "SaiAttributeList.h" #include "crmorch.h" #include "dbconnector.h" -#include #include "logger.h" #include "orch.h" #include "p4orch/p4orch_util.h" @@ -324,14 +324,15 @@ ReturnCode NeighborManager::processDeleteRequest(const std::string &neighbor_key return status; } -ReturnCode NeighborManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode NeighborManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { - std::string router_intf_id, neighbor_id; + std::string router_intf_id, neighbor_id; swss::IpAddress neighbor; try { - nlohmann::json j = nlohmann::json::parse(json_key); + nlohmann::json j = nlohmann::json::parse(json_key); if (j.find(prependMatchField(p4orch::kRouterInterfaceId)) != j.end()) { router_intf_id = j.at(prependMatchField(p4orch::kRouterInterfaceId)).get(); @@ -350,7 +351,8 @@ ReturnCode NeighborManager::getSaiObject(const std::string &json_key, sai_object } else { - SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kRouterInterfaceId); + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", + p4orch::kRouterInterfaceId); } } catch (std::exception &ex) diff --git a/orchagent/p4orch/neighbor_manager.h b/orchagent/p4orch/neighbor_manager.h index 0022d3a8cc..229dcc41d1 100644 --- a/orchagent/p4orch/neighbor_manager.h +++ b/orchagent/p4orch/neighbor_manager.h @@ -52,7 +52,8 @@ class NeighborManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: ReturnCodeOr deserializeNeighborEntry(const std::string &key, diff --git a/orchagent/p4orch/next_hop_manager.cpp b/orchagent/p4orch/next_hop_manager.cpp index 1614a266f6..f55c83534a 100644 --- a/orchagent/p4orch/next_hop_manager.cpp +++ b/orchagent/p4orch/next_hop_manager.cpp @@ -1,5 +1,6 @@ #include "p4orch/next_hop_manager.h" +#include #include #include #include @@ -8,7 +9,6 @@ #include "crmorch.h" #include "dbconnector.h" #include "ipaddress.h" -#include #include "logger.h" #include "p4orch/p4orch.h" #include "p4orch/p4orch_util.h" @@ -147,13 +147,14 @@ ReturnCodeOr> NextHopManager::getSaiAttrs(const P4N return next_hop_attrs; } -ReturnCode NextHopManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode NextHopManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { - std::string value; + std::string value; try { - nlohmann::json j = nlohmann::json::parse(json_key); + nlohmann::json j = nlohmann::json::parse(json_key); if (j.find(prependMatchField(p4orch::kNexthopId)) != j.end()) { value = j.at(prependMatchField(p4orch::kNexthopId)).get(); diff --git a/orchagent/p4orch/next_hop_manager.h b/orchagent/p4orch/next_hop_manager.h index 7bacdad534..aac6f5e444 100644 --- a/orchagent/p4orch/next_hop_manager.h +++ b/orchagent/p4orch/next_hop_manager.h @@ -60,7 +60,8 @@ class NextHopManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: // Gets the internal cached next hop entry by its key. diff --git a/orchagent/p4orch/object_manager_interface.h b/orchagent/p4orch/object_manager_interface.h index 966288a156..1d44990edc 100644 --- a/orchagent/p4orch/object_manager_interface.h +++ b/orchagent/p4orch/object_manager_interface.h @@ -18,5 +18,6 @@ class ObjectManagerInterface // For sai extension objects depending on a sai object // return sai object id for a given table with a given key - virtual ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) = 0; + virtual ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) = 0; }; diff --git a/orchagent/p4orch/p4orch.cpp b/orchagent/p4orch/p4orch.cpp index eca0918171..f1e6bd4702 100644 --- a/orchagent/p4orch/p4orch.cpp +++ b/orchagent/p4orch/p4orch.cpp @@ -8,17 +8,17 @@ #include "copporch.h" #include "logger.h" #include "orch.h" -#include "p4orch/p4orch_util.h" -#include "p4orch/tables_definition_manager.h" #include "p4orch/acl_rule_manager.h" #include "p4orch/acl_table_manager.h" +#include "p4orch/ext_tables_manager.h" #include "p4orch/gre_tunnel_manager.h" #include "p4orch/l3_admit_manager.h" #include "p4orch/neighbor_manager.h" #include "p4orch/next_hop_manager.h" +#include "p4orch/p4orch_util.h" #include "p4orch/route_manager.h" #include "p4orch/router_interface_manager.h" -#include "p4orch/ext_tables_manager.h" +#include "p4orch/tables_definition_manager.h" #include "portsorch.h" #include "return_code.h" #include "sai_serialize.h" @@ -142,7 +142,7 @@ void P4Orch::doTask(Consumer &consumer) else { auto status = ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Failed to find P4Orch Manager for " << table_name << " P4RT DB table"; + << "Failed to find P4Orch Manager for " << table_name << " P4RT DB table"; SWSS_LOG_ERROR("%s", status.message().c_str()); m_publisher.publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), status); diff --git a/orchagent/p4orch/p4orch.h b/orchagent/p4orch/p4orch.h index 9385346d20..cc02052830 100644 --- a/orchagent/p4orch/p4orch.h +++ b/orchagent/p4orch/p4orch.h @@ -10,9 +10,9 @@ #include "notificationconsumer.h" #include "notifier.h" #include "orch.h" -#include "p4orch/tables_definition_manager.h" #include "p4orch/acl_rule_manager.h" #include "p4orch/acl_table_manager.h" +#include "p4orch/ext_tables_manager.h" #include "p4orch/gre_tunnel_manager.h" #include "p4orch/l3_admit_manager.h" #include "p4orch/mirror_session_manager.h" @@ -22,22 +22,21 @@ #include "p4orch/p4oidmapper.h" #include "p4orch/route_manager.h" #include "p4orch/router_interface_manager.h" +#include "p4orch/tables_definition_manager.h" #include "p4orch/wcmp_manager.h" -#include "p4orch/ext_tables_manager.h" #include "response_publisher.h" #include "vrforch.h" static const std::map FixedTablesMap = { - {"router_interface_table", APP_P4RT_ROUTER_INTERFACE_TABLE_NAME }, - {"neighbor_table", APP_P4RT_NEIGHBOR_TABLE_NAME}, - {"nexthop_table", APP_P4RT_NEXTHOP_TABLE_NAME}, - {"wcmp_group_table", APP_P4RT_WCMP_GROUP_TABLE_NAME}, - {"ipv4_table", APP_P4RT_IPV4_TABLE_NAME}, - {"ipv6_table", APP_P4RT_IPV6_TABLE_NAME}, - {"mirror_session_table", APP_P4RT_MIRROR_SESSION_TABLE_NAME}, - {"l3_admit_table", APP_P4RT_L3_ADMIT_TABLE_NAME}, - {"tunnel_table", APP_P4RT_TUNNEL_TABLE_NAME} -}; + {"router_interface_table", APP_P4RT_ROUTER_INTERFACE_TABLE_NAME}, + {"neighbor_table", APP_P4RT_NEIGHBOR_TABLE_NAME}, + {"nexthop_table", APP_P4RT_NEXTHOP_TABLE_NAME}, + {"wcmp_group_table", APP_P4RT_WCMP_GROUP_TABLE_NAME}, + {"ipv4_table", APP_P4RT_IPV4_TABLE_NAME}, + {"ipv6_table", APP_P4RT_IPV6_TABLE_NAME}, + {"mirror_session_table", APP_P4RT_MIRROR_SESSION_TABLE_NAME}, + {"l3_admit_table", APP_P4RT_L3_ADMIT_TABLE_NAME}, + {"tunnel_table", APP_P4RT_TUNNEL_TABLE_NAME}}; class P4Orch : public Orch { @@ -55,7 +54,6 @@ class P4Orch : public Orch // m_p4TableToManagerMap: P4 APP DB table name, P4 Object Manager std::unordered_map m_p4TableToManagerMap; - private: void doTask(Consumer &consumer); diff --git a/orchagent/p4orch/p4orch_util.cpp b/orchagent/p4orch/p4orch_util.cpp index b2ea0a762b..dd0a4171ad 100644 --- a/orchagent/p4orch/p4orch_util.cpp +++ b/orchagent/p4orch/p4orch_util.cpp @@ -1,6 +1,6 @@ -#include "p4orch/p4orch.h" #include "p4orch/p4orch_util.h" +#include "p4orch/p4orch.h" #include "schema.h" using ::p4orch::kTableKeyDelimiter; @@ -116,9 +116,7 @@ ActionInfo *getTableActionInfo(TableInfo *table, const std::string &action_name) std::string KeyGenerator::generateTablesInfoKey(const std::string &context) { - std::map fv_map = { - {"context", context} - }; + std::map fv_map = {{"context", context}}; return generateKey(fv_map); } diff --git a/orchagent/p4orch/p4orch_util.h b/orchagent/p4orch/p4orch_util.h index f95a9fd8eb..9cfcf53a82 100644 --- a/orchagent/p4orch/p4orch_util.h +++ b/orchagent/p4orch/p4orch_util.h @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include #include "ipaddress.h" #include "ipprefix.h" @@ -16,7 +16,6 @@ extern "C" { #include "saitypes.h" } - namespace p4orch { @@ -110,48 +109,48 @@ std::string prependParamField(const std::string &str); struct ActionParamInfo { - std::string name; - std::string fieldtype; - std::string datatype; - std::unordered_map table_reference_map; + std::string name; + std::string fieldtype; + std::string datatype; + std::unordered_map table_reference_map; }; struct ActionInfo { - std::string name; + std::string name; std::unordered_map params; - bool refers_to; + bool refers_to; }; struct TableMatchInfo { - std::string name; - std::string fieldtype; - std::string datatype; - std::unordered_map table_reference_map; + std::string name; + std::string fieldtype; + std::string datatype; + std::unordered_map table_reference_map; }; /** - * Dervied table definition + * Dervied table definition * This is a derived state out of table definition provided by P4RT-APP */ struct TableInfo { - std::string name; - int id; - int precedence; - std::unordered_map match_fields; - std::unordered_map action_fields; - bool counter_bytes_enabled; - bool counter_packets_enabled; - std::vector action_ref_tables; - // list of tables across all actions, of current table, refer to + std::string name; + int id; + int precedence; + std::unordered_map match_fields; + std::unordered_map action_fields; + bool counter_bytes_enabled; + bool counter_packets_enabled; + std::vector action_ref_tables; + // list of tables across all actions, of current table, refer to }; /** * table-name to table-definition map */ -typedef std::unordered_map TableInfoMap; +typedef std::unordered_map TableInfoMap; struct TablesInfoAppDbEntry { @@ -159,7 +158,6 @@ struct TablesInfoAppDbEntry std::string info; }; - struct P4RouterInterfaceAppDbEntry { std::string router_interface_id; @@ -296,8 +294,8 @@ struct P4AclRuleAppDbEntry struct DepObject { sai_object_type_t sai_object; - std::string key; - sai_object_id_t oid; + std::string key; + sai_object_id_t oid; }; struct P4ExtTableAppDbEntry @@ -309,7 +307,6 @@ struct P4ExtTableAppDbEntry std::unordered_map action_dep_objects; }; - TableInfo *getTableInfo(const std::string &table_name); ActionInfo *getTableActionInfo(TableInfo *table, const std::string &action_name); diff --git a/orchagent/p4orch/route_manager.cpp b/orchagent/p4orch/route_manager.cpp index bc8f3bbcd8..c50b3bb4b9 100644 --- a/orchagent/p4orch/route_manager.cpp +++ b/orchagent/p4orch/route_manager.cpp @@ -1,6 +1,7 @@ #include "p4orch/route_manager.h" #include +#include #include #include #include @@ -11,7 +12,6 @@ #include "converter.h" #include "crmorch.h" #include "dbconnector.h" -#include #include "logger.h" #include "p4orch/p4orch_util.h" #include "sai_serialize.h" @@ -837,7 +837,8 @@ std::vector RouteManager::deleteRouteEntries(const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: // Applies route entry updates from src to dest. The merged result will be diff --git a/orchagent/p4orch/router_interface_manager.cpp b/orchagent/p4orch/router_interface_manager.cpp index e174b5ec7e..de32576e68 100644 --- a/orchagent/p4orch/router_interface_manager.cpp +++ b/orchagent/p4orch/router_interface_manager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -10,7 +11,6 @@ #include "SaiAttributeList.h" #include "dbconnector.h" #include "directory.h" -#include #include "logger.h" #include "orch.h" #include "p4orch/p4orch_util.h" @@ -337,13 +337,14 @@ ReturnCode RouterInterfaceManager::processDeleteRequest(const std::string &route return status; } -ReturnCode RouterInterfaceManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode RouterInterfaceManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { - std::string value; + std::string value; try { - nlohmann::json j = nlohmann::json::parse(json_key); + nlohmann::json j = nlohmann::json::parse(json_key); if (j.find(prependMatchField(p4orch::kRouterInterfaceId)) != j.end()) { value = j.at(prependMatchField(p4orch::kRouterInterfaceId)).get(); @@ -353,7 +354,8 @@ ReturnCode RouterInterfaceManager::getSaiObject(const std::string &json_key, sai } else { - SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", p4orch::kRouterInterfaceId); + SWSS_LOG_ERROR("%s match parameter absent: required for dependent object query", + p4orch::kRouterInterfaceId); } } catch (std::exception &ex) diff --git a/orchagent/p4orch/router_interface_manager.h b/orchagent/p4orch/router_interface_manager.h index 427400e9c0..f33f443979 100644 --- a/orchagent/p4orch/router_interface_manager.h +++ b/orchagent/p4orch/router_interface_manager.h @@ -52,7 +52,8 @@ class RouterInterfaceManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: ReturnCodeOr deserializeRouterIntfEntry( diff --git a/orchagent/p4orch/tables_definition_manager.cpp b/orchagent/p4orch/tables_definition_manager.cpp index c0fab4265a..1da15028e5 100644 --- a/orchagent/p4orch/tables_definition_manager.cpp +++ b/orchagent/p4orch/tables_definition_manager.cpp @@ -1,36 +1,29 @@ #include "p4orch/tables_definition_manager.h" #include +#include #include #include #include #include #include "directory.h" -#include #include "logger.h" -#include "tokenize.h" #include "orch.h" #include "p4orch/p4orch.h" #include "p4orch/p4orch_util.h" +#include "tokenize.h" extern "C" { #include "saitypes.h" } - extern Directory gDirectory; extern P4Orch *gP4Orch; -const std::map format_datatype_map = -{ - {"MAC", "SAI_ATTR_VALUE_TYPE_MAC"}, - {"IPV4", "SAI_ATTR_VALUE_TYPE_IPV4"}, - {"IPV6", "SAI_ATTR_VALUE_TYPE_IPV6"} -}; +const std::map format_datatype_map = { + {"MAC", "SAI_ATTR_VALUE_TYPE_MAC"}, {"IPV4", "SAI_ATTR_VALUE_TYPE_IPV4"}, {"IPV6", "SAI_ATTR_VALUE_TYPE_IPV6"}}; - -std::string -BitwidthToDatatype (int bitwidth) +std::string BitwidthToDatatype(int bitwidth) { std::string datatype = "SAI_ATTR_VALUE_TYPE_CHARDATA"; @@ -58,8 +51,7 @@ BitwidthToDatatype (int bitwidth) return datatype; } -std::string -parseBitwidthToDatatype (const nlohmann::json &json) +std::string parseBitwidthToDatatype(const nlohmann::json &json) { int bitwidth; std::string datatype = "SAI_ATTR_VALUE_TYPE_CHARDATA"; @@ -73,8 +65,7 @@ parseBitwidthToDatatype (const nlohmann::json &json) return datatype; } -std::string -parseFormatToDatatype (const nlohmann::json &json, std::string datatype) +std::string parseFormatToDatatype(const nlohmann::json &json, std::string datatype) { std::string format; @@ -92,8 +83,7 @@ parseFormatToDatatype (const nlohmann::json &json, std::string datatype) return datatype; } -ReturnCode -parseTableMatchReferences (const nlohmann::json &match_json, TableMatchInfo &match) +ReturnCode parseTableMatchReferences(const nlohmann::json &match_json, TableMatchInfo &match) { std::string table, field; @@ -110,7 +100,8 @@ parseTableMatchReferences (const nlohmann::json &match_json, TableMatchInfo &mat catch (std::exception &ex) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "can not parse tables from app-db supplied table definition info"; + << "can not parse tables from app-db supplied table definition " + "info"; } } } @@ -118,8 +109,7 @@ parseTableMatchReferences (const nlohmann::json &match_json, TableMatchInfo &mat return ReturnCode(); } -ReturnCode -parseActionParamReferences (const nlohmann::json ¶m_json, ActionParamInfo ¶m) +ReturnCode parseActionParamReferences(const nlohmann::json ¶m_json, ActionParamInfo ¶m) { std::string table, field; @@ -136,7 +126,8 @@ parseActionParamReferences (const nlohmann::json ¶m_json, ActionParamInfo &p catch (std::exception &ex) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "can not parse tables from app-db supplied table definition info"; + << "can not parse tables from app-db supplied table definition " + "info"; } } } @@ -144,8 +135,7 @@ parseActionParamReferences (const nlohmann::json ¶m_json, ActionParamInfo &p return ReturnCode(); } -ReturnCode -parseTableActionParams (const nlohmann::json &action_json, ActionInfo &action) +ReturnCode parseTableActionParams(const nlohmann::json &action_json, ActionInfo &action) { action.refers_to = false; if (action_json.find(p4orch::kActionParams) != action_json.end()) @@ -167,7 +157,8 @@ parseTableActionParams (const nlohmann::json &action_json, ActionInfo &action) if (!param.table_reference_map.empty()) { /** - * Helps avoid walk of action parameters if this is set to false at action level + * Helps avoid walk of action parameters if this is set to false at + * action level */ action.refers_to = true; } @@ -175,7 +166,8 @@ parseTableActionParams (const nlohmann::json &action_json, ActionInfo &action) catch (std::exception &ex) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "can not parse tables from app-db supplied table definition info"; + << "can not parse tables from app-db supplied table definition " + "info"; } } } @@ -183,8 +175,7 @@ parseTableActionParams (const nlohmann::json &action_json, ActionInfo &action) return ReturnCode(); } -ReturnCode -parseTableCounter (const nlohmann::json &table_json, TableInfo &table) +ReturnCode parseTableCounter(const nlohmann::json &table_json, TableInfo &table) { if (table_json.find(p4orch::kCounterUnit) != table_json.end()) { @@ -207,8 +198,7 @@ parseTableCounter (const nlohmann::json &table_json, TableInfo &table) return ReturnCode(); } -ReturnCode -parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) +ReturnCode parseTablesInfo(const nlohmann::json &info_json, TablesInfo &info_entry) { ReturnCode status; int table_id; @@ -216,8 +206,7 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) if (info_json.find(p4orch::kTables) == info_json.end()) { - return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "no tables in app-db supplied table definition info"; + return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) << "no tables in app-db supplied table definition info"; } for (const auto &table_json : info_json[p4orch::kTables]) @@ -230,18 +219,18 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) catch (std::exception &ex) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "can not parse tables from app-db supplied table definition info"; + << "can not parse tables from app-db supplied table definition " + "info"; } - - TableInfo table = {}; + TableInfo table = {}; table.name = table_name; - table.id = table_id; + table.id = table_id; try { for (const auto &match_json : table_json[p4orch::kmatchFields]) { - TableMatchInfo match = {}; + TableMatchInfo match = {}; std::string match_name; match_name = match_json.at(p4orch::kName).get(); @@ -254,7 +243,7 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) for (const auto &action_json : table_json[p4orch::kActions]) { - ActionInfo action = {}; + ActionInfo action = {}; std::string action_name; action_name = action_json.at(p4orch::kAlias).get(); @@ -263,23 +252,21 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) table.action_fields[action_name] = action; /** - * If any parameter of action refers to another table, add that one in the - * cross-reference list of current table + * If any parameter of action refers to another table, add that one in + * the cross-reference list of current table */ - for (auto param_it = action.params.begin(); - param_it != action.params.end(); param_it++) + for (auto param_it = action.params.begin(); param_it != action.params.end(); param_it++) { ActionParamInfo action_param = param_it->second; for (auto ref_it = action_param.table_reference_map.begin(); - ref_it != action_param.table_reference_map.end(); ref_it++) + ref_it != action_param.table_reference_map.end(); ref_it++) { - if (std::find(table.action_ref_tables.begin(), - table.action_ref_tables.end(), - ref_it->first) == table.action_ref_tables.end()) + if (std::find(table.action_ref_tables.begin(), table.action_ref_tables.end(), ref_it->first) == + table.action_ref_tables.end()) { table.action_ref_tables.push_back(ref_it->first); } - } + } } } @@ -288,10 +275,9 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) catch (std::exception &ex) { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "can not parse table " << QuotedVar(table_name.c_str()) << "match fields"; + << "can not parse table " << QuotedVar(table_name.c_str()) << "match fields"; } - info_entry.m_tableIdNameMap[std::to_string(table_id)] = table_name; info_entry.m_tableInfoMap[table_name] = table; } @@ -299,7 +285,6 @@ parseTablesInfo (const nlohmann::json &info_json, TablesInfo &info_entry) return ReturnCode(); } - ReturnCodeOr TablesDefnManager::deserializeTablesInfoEntry( const std::string &key, const std::vector &attributes) { @@ -327,7 +312,7 @@ ReturnCodeOr TablesDefnManager::deserializeTablesInfoEntry else { return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM) - << "Unexpected field " << QuotedVar(field) << " in table entry"; + << "Unexpected field " << QuotedVar(field) << " in table entry"; } } @@ -416,14 +401,13 @@ ReturnCode TablesDefnManager::processDeleteRequest(const std::string &context_ke return ReturnCode(); } -ReturnCode TablesDefnManager::getSaiObject(const std::string &json_key, - sai_object_type_t &object_type, std::string &object_key) +ReturnCode TablesDefnManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { return StatusCode::SWSS_RC_INVALID_PARAM; } -std::unordered_map> -createGraph (std::vector> preReq) +std::unordered_map> createGraph(std::vector> preReq) { std::unordered_map> graph; @@ -443,8 +427,7 @@ createGraph (std::vector> preReq) return graph; } -std::unordered_map -computeIndegree (std::unordered_map> &graph) +std::unordered_map computeIndegree(std::unordered_map> &graph) { std::unordered_map degrees; @@ -467,19 +450,16 @@ computeIndegree (std::unordered_map> &graph) return degrees; } - -std::vector -findTablePrecedence (int tables, std::vector> preReq, TablesInfo *tables_info) +std::vector findTablePrecedence(int tables, std::vector> preReq, TablesInfo *tables_info) { std::unordered_map> graph = createGraph(preReq); std::unordered_map degrees = computeIndegree(graph); std::vector visited; std::vector toposort; - std::queue zeros; + std::queue zeros; // initialize queue with tables having no dependencies - for (auto table_it = tables_info->m_tableInfoMap.begin(); - table_it != tables_info->m_tableInfoMap.end(); table_it++) + for (auto table_it = tables_info->m_tableInfoMap.begin(); table_it != tables_info->m_tableInfoMap.end(); table_it++) { TableInfo table_info = table_it->second; if (degrees.find(table_info.id) == degrees.end()) @@ -491,7 +471,8 @@ findTablePrecedence (int tables, std::vector> preReq, Tables for (int i = 0; i < tables; i++) { - // Err input data like possible cyclic dependencies, could not build precedence order + // Err input data like possible cyclic dependencies, could not build + // precedence order if (zeros.empty()) { SWSS_LOG_ERROR("Filed to build table precedence order"); @@ -530,21 +511,19 @@ findTablePrecedence (int tables, std::vector> preReq, Tables return toposort; } - -void -buildTablePrecedence (TablesInfo *tables_info) +void buildTablePrecedence(TablesInfo *tables_info) { std::vector> preReq; std::vector orderedTables; int tables = 0; - if (!tables_info) { + if (!tables_info) + { return; } // build dependencies - for (auto table_it = tables_info->m_tableInfoMap.begin(); - table_it != tables_info->m_tableInfoMap.end(); table_it++) + for (auto table_it = tables_info->m_tableInfoMap.begin(); table_it != tables_info->m_tableInfoMap.end(); table_it++) { TableInfo table_info = table_it->second; tables++; @@ -552,18 +531,18 @@ buildTablePrecedence (TablesInfo *tables_info) for (std::size_t i = 0; i < table_info.action_ref_tables.size(); i++) { /** - * For now processing precedence order is only amongst extension tables - * Skip fixed tables, include them in precedence calculations when fixed - * and extension tables processing precedence may be interleaved - */ + * For now processing precedence order is only amongst extension tables + * Skip fixed tables, include them in precedence calculations when fixed + * and extension tables processing precedence may be interleaved + */ if (FixedTablesMap.find(table_info.action_ref_tables[i]) != FixedTablesMap.end()) { continue; } TableInfo ref_table_info = tables_info->m_tableInfoMap[table_info.action_ref_tables[i]]; - if (std::find(preReq.begin(), preReq.end(), - std::make_pair(table_info.id, ref_table_info.id)) == preReq.end()) + if (std::find(preReq.begin(), preReq.end(), std::make_pair(table_info.id, ref_table_info.id)) == + preReq.end()) { preReq.push_back(std::make_pair(table_info.id, ref_table_info.id)); } @@ -573,7 +552,8 @@ buildTablePrecedence (TablesInfo *tables_info) // find precedence of tables based on dependencies orderedTables = findTablePrecedence(tables, preReq, tables_info); - // update each table with calculated precedence value and build table precedence map + // update each table with calculated precedence value and build table + // precedence map for (std::size_t i = 0; i < orderedTables.size(); i++) { auto table_id = orderedTables[i]; @@ -596,7 +576,6 @@ buildTablePrecedence (TablesInfo *tables_info) return; } - void TablesDefnManager::enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) { m_entries.push_back(entry); @@ -621,7 +600,8 @@ void TablesDefnManager::drain() SWSS_LOG_ERROR("Unable to deserialize APP DB entry with key %s: %s", QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); + status, + /*replace=*/true); continue; } auto &app_db_entry = *app_db_entry_or; @@ -629,10 +609,12 @@ void TablesDefnManager::drain() status = validateTablesInfoAppDbEntry(app_db_entry); if (!status.ok()) { - SWSS_LOG_ERROR("Validation failed for tables definition APP DB entry with key %s: %s", + SWSS_LOG_ERROR("Validation failed for tables definition APP DB entry with key %s: " + "%s", QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); + status, + /*replace=*/true); continue; } @@ -665,15 +647,16 @@ void TablesDefnManager::drain() } if (!status.ok()) { - SWSS_LOG_ERROR("Processing failed for tables definition APP DB entry with key %s: %s", + SWSS_LOG_ERROR("Processing failed for tables definition APP DB entry with key %s: " + "%s", QuotedVar(table_name + ":" + key).c_str(), status.message().c_str()); } else { buildTablePrecedence(gP4Orch->tablesinfo); } - m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), - status, /*replace=*/true); + m_publisher->publish(APP_P4RT_TABLE_NAME, kfvKey(key_op_fvs_tuple), kfvFieldsValues(key_op_fvs_tuple), status, + /*replace=*/true); } m_entries.clear(); } diff --git a/orchagent/p4orch/tables_definition_manager.h b/orchagent/p4orch/tables_definition_manager.h index 088b832bcd..85ca363bf5 100644 --- a/orchagent/p4orch/tables_definition_manager.h +++ b/orchagent/p4orch/tables_definition_manager.h @@ -1,12 +1,12 @@ #pragma once #include +#include #include #include #include #include "macaddress.h" -#include #include "orch.h" #include "p4orch/object_manager_interface.h" #include "p4orch/p4oidmapper.h" @@ -23,8 +23,8 @@ extern "C" */ struct TablesInfo { - std::string context; - nlohmann::json info; + std::string context; + nlohmann::json info; std::unordered_map m_tableIdNameMap; std::unordered_map m_tableInfoMap; std::map m_tablePrecedenceMap; @@ -59,11 +59,12 @@ class TablesDefnManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; private: - ReturnCodeOr deserializeTablesInfoEntry( - const std::string &key, const std::vector &attributes); + ReturnCodeOr deserializeTablesInfoEntry(const std::string &key, + const std::vector &attributes); TablesInfo *getTablesInfoEntry(const std::string &context_key); ReturnCode createTablesInfo(const std::string &context_key, TablesInfo &tablesinfo_entry); ReturnCode removeTablesInfo(const std::string &context_key); diff --git a/orchagent/p4orch/tests/Makefile.am b/orchagent/p4orch/tests/Makefile.am index d541bbe637..4aeb07f1ae 100644 --- a/orchagent/p4orch/tests/Makefile.am +++ b/orchagent/p4orch/tests/Makefile.am @@ -23,8 +23,8 @@ CFLAGS_TSAN = -fsanitize=thread CFLAGS_USAN = -fsanitize=undefined p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ - $(ORCHAGENT_DIR)/vrforch.cpp \ $(ORCHAGENT_DIR)/vxlanorch.cpp \ + $(ORCHAGENT_DIR)/vrforch.cpp \ $(ORCHAGENT_DIR)/copporch.cpp \ $(ORCHAGENT_DIR)/switch/switch_capabilities.cpp \ $(ORCHAGENT_DIR)/switch/switch_helper.cpp \ @@ -52,6 +52,7 @@ p4orch_tests_SOURCES = $(ORCHAGENT_DIR)/orch.cpp \ $(P4ORCH_DIR)/l3_admit_manager.cpp \ $(P4ORCH_DIR)/ext_tables_manager.cpp \ $(top_srcdir)/tests/mock_tests/fake_response_publisher.cpp \ + fake_routeorch.cpp \ fake_portorch.cpp \ fake_crmorch.cpp \ fake_flexcounterorch.cpp \ diff --git a/orchagent/p4orch/tests/acl_manager_test.cpp b/orchagent/p4orch/tests/acl_manager_test.cpp index 5827fd1a13..13856c07ea 100644 --- a/orchagent/p4orch/tests/acl_manager_test.cpp +++ b/orchagent/p4orch/tests/acl_manager_test.cpp @@ -3,13 +3,13 @@ #include #include +#include #include #include "acl_rule_manager.h" #include "acl_table_manager.h" #include "acl_util.h" #include "acltable.h" -#include #include "mock_sai_acl.h" #include "mock_sai_hostif.h" #include "mock_sai_policer.h" @@ -240,7 +240,7 @@ std::string BuildMatchFieldJsonStrKindComposite(std::vector elem { nlohmann::json match_json; match_json[kAclMatchFieldKind] = kAclMatchFieldKindComposite; - for (const auto element : elements) + for (const auto &element : elements) { match_json[kAclMatchFieldElements].push_back(element); } diff --git a/orchagent/p4orch/tests/fake_flexcounterorch.cpp b/orchagent/p4orch/tests/fake_flexcounterorch.cpp index 39f742e14c..91d6be3d14 100644 --- a/orchagent/p4orch/tests/fake_flexcounterorch.cpp +++ b/orchagent/p4orch/tests/fake_flexcounterorch.cpp @@ -1,12 +1,10 @@ #include "copporch.h" #include "flexcounterorch.h" -FlexCounterOrch::FlexCounterOrch(swss::DBConnector *db, std::vector &tableNames) : - Orch(db, tableNames), - m_flexCounterConfigTable(db, CFG_FLEX_COUNTER_TABLE_NAME), - m_bufferQueueConfigTable(db, CFG_BUFFER_QUEUE_TABLE_NAME), - m_bufferPgConfigTable(db, CFG_BUFFER_PG_TABLE_NAME), - m_deviceMetadataConfigTable(db, CFG_DEVICE_METADATA_TABLE_NAME) +FlexCounterOrch::FlexCounterOrch(swss::DBConnector *db, std::vector &tableNames) + : Orch(db, tableNames), m_flexCounterConfigTable(db, CFG_FLEX_COUNTER_TABLE_NAME), + m_bufferQueueConfigTable(db, CFG_BUFFER_QUEUE_TABLE_NAME), m_bufferPgConfigTable(db, CFG_BUFFER_PG_TABLE_NAME), + m_deviceMetadataConfigTable(db, CFG_DEVICE_METADATA_TABLE_NAME) { } diff --git a/orchagent/p4orch/tests/fake_portorch.cpp b/orchagent/p4orch/tests/fake_portorch.cpp index b8a2f56fde..a34a30eb4b 100644 --- a/orchagent/p4orch/tests/fake_portorch.cpp +++ b/orchagent/p4orch/tests/fake_portorch.cpp @@ -185,7 +185,7 @@ void PortsOrch::generateQueueMap(std::map queues { } -void PortsOrch::generateQueueMapPerPort(const Port& port, FlexCounterQueueStates& queuesState, bool voq) +void PortsOrch::generateQueueMapPerPort(const Port &port, FlexCounterQueueStates &queuesState, bool voq) { } @@ -201,15 +201,15 @@ void PortsOrch::generatePriorityGroupMap(std::map p { } -void PortsOrch::generatePriorityGroupMapPerPort(const Port& port, FlexCounterPgStates& pgsState) +void PortsOrch::generatePriorityGroupMapPerPort(const Port &port, FlexCounterPgStates &pgsState) { } -void PortsOrch::createPortBufferPgCounters(const Port& port, string pgs) +void PortsOrch::createPortBufferPgCounters(const Port &port, string pgs) { } -void PortsOrch::removePortBufferPgCounters(const Port& port, string pgs) +void PortsOrch::removePortBufferPgCounters(const Port &port, string pgs) { } @@ -591,7 +591,8 @@ bool PortsOrch::setGearboxPortsAttr(const Port &port, sai_port_attr_t id, void * return true; } -bool PortsOrch::setGearboxPortAttr(const Port &port, dest_port_type_t port_type, sai_port_attr_t id, void *value, bool override_fec) +bool PortsOrch::setGearboxPortAttr(const Port &port, dest_port_type_t port_type, sai_port_attr_t id, void *value, + bool override_fec) { return true; } @@ -621,7 +622,8 @@ task_process_status PortsOrch::setPortInterfaceType(Port &port, sai_port_interfa return task_success; } -task_process_status PortsOrch::setPortAdvInterfaceTypes(Port &port, std::set &interface_types) +task_process_status PortsOrch::setPortAdvInterfaceTypes(Port &port, + std::set &interface_types) { return task_success; } diff --git a/orchagent/p4orch/tests/fake_routeorch.cpp b/orchagent/p4orch/tests/fake_routeorch.cpp new file mode 100644 index 0000000000..74d7c3de8e --- /dev/null +++ b/orchagent/p4orch/tests/fake_routeorch.cpp @@ -0,0 +1,232 @@ +extern "C" +{ +#include "sai.h" +} +#include +#include "routeorch.h" +#include "nhgorch.h" + +/* Default maximum number of next hop groups */ +#define DEFAULT_NUMBER_OF_ECMP_GROUPS 128 +#define DEFAULT_MAX_ECMP_GROUP_SIZE 32 + +extern sai_object_id_t gVirtualRouterId; +extern sai_object_id_t gSwitchId; + +extern sai_next_hop_group_api_t* sai_next_hop_group_api; +extern sai_route_api_t* sai_route_api; +extern sai_mpls_api_t* sai_mpls_api; +extern sai_switch_api_t* sai_switch_api; +extern size_t gMaxBulkSize; + +RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, SwitchOrch *switchOrch, NeighOrch *neighOrch, IntfsOrch *intfsOrch, VRFOrch *vrfOrch, FgNhgOrch *fgNhgOrch, Srv6Orch *srv6Orch) : + gRouteBulker(sai_route_api, gMaxBulkSize), + gLabelRouteBulker(sai_mpls_api, gMaxBulkSize), + gNextHopGroupMemberBulker(sai_next_hop_group_api, gSwitchId, gMaxBulkSize), + Orch(db, tableNames) +{ + +} + +std::string RouteOrch::getLinkLocalEui64Addr(const MacAddress &mac) +{ + SWSS_LOG_ENTER(); + + string ip_prefix; + const uint8_t *gmac = mac.getMac(); + + uint8_t eui64_interface_id[EUI64_INTF_ID_LEN]; + char ipv6_ll_addr[INET6_ADDRSTRLEN] = {0}; + + /* Link-local IPv6 address autogenerated by kernel with eui64 interface-id + * derived from the MAC address of the host interface. + */ + eui64_interface_id[0] = gmac[0] ^ 0x02; + eui64_interface_id[1] = gmac[1]; + eui64_interface_id[2] = gmac[2]; + eui64_interface_id[3] = 0xff; + eui64_interface_id[4] = 0xfe; + eui64_interface_id[5] = gmac[3]; + eui64_interface_id[6] = gmac[4]; + eui64_interface_id[7] = gmac[5]; + + snprintf(ipv6_ll_addr, INET6_ADDRSTRLEN, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x", + eui64_interface_id[0], eui64_interface_id[1], eui64_interface_id[2], + eui64_interface_id[3], eui64_interface_id[4], eui64_interface_id[5], + eui64_interface_id[6], eui64_interface_id[7]); + + ip_prefix = string(ipv6_ll_addr); + + return ip_prefix; +} + +void RouteOrch::addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix) +{ + +} + +void RouteOrch::delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix) +{ + +} + +void RouteOrch::updateDefRouteState(string ip, bool add) +{ +} + +bool RouteOrch::hasNextHopGroup(const NextHopGroupKey& nexthops) const +{ + return true; +} + +void RouteOrch::attach(Observer *observer, const IpAddress& dstAddr, sai_object_id_t vrf_id) +{ +} + +void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr, sai_object_id_t vrf_id) +{ + +} + +bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t& count) +{ + return true; +} + +bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t& count) +{ + return true; +} + +void RouteOrch::doTask(Consumer& consumer) +{ + +} + +void RouteOrch::notifyNextHopChangeObservers(sai_object_id_t vrf_id, const IpPrefix &prefix, const NextHopGroupKey &nexthops, bool add) +{ + +} + +void RouteOrch::increaseNextHopRefCount(const NextHopGroupKey &nexthops) +{ + +} + +void RouteOrch::decreaseNextHopRefCount(const NextHopGroupKey &nexthops) +{ + +} + +bool RouteOrch::isRefCounterZero(const NextHopGroupKey &nexthops) const +{ + return true; +} + +bool RouteOrch::createFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id, vector &nhg_attrs) +{ + return true; +} + +bool RouteOrch::removeFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id) +{ + return true; +} + +bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) +{ + return true; +} + +bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) +{ + return true; +} + +void RouteOrch::addNextHopRoute(const NextHopKey& nextHop, const RouteKey& routeKey) +{ + +} + +void RouteOrch::removeNextHopRoute(const NextHopKey& nextHop, const RouteKey& routeKey) +{ + +} + +bool RouteOrch::updateNextHopRoutes(const NextHopKey& nextHop, uint32_t& numRoutes) +{ + return true; +} + +bool RouteOrch::getRoutesForNexthop(std::set& routeKeys, const NextHopKey& nexthopKey) +{ + return true; +} + +void RouteOrch::addTempRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) +{ + +} + +bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) +{ + return true; +} + +bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey &nextHops) +{ + return true; +} + +bool RouteOrch::removeRoute(RouteBulkContext& ctx) +{ + return true; +} + +bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) +{ + return true; +} + +bool RouteOrch::createRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextHop) +{ + return true; +} + +bool RouteOrch::deleteRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextHop) +{ + return true; +} + +bool RouteOrch::removeOverlayNextHops(sai_object_id_t vrf_id, const NextHopGroupKey &ol_nextHops) +{ + return true; +} + +void RouteOrch::increaseNextHopGroupCount() +{ +} + +void RouteOrch::decreaseNextHopGroupCount() +{ +} + +bool RouteOrch::checkNextHopGroupCount() +{ + return true; +} + + +void RouteOrch::incNhgRefCount(const std::string &nhg_index) +{ +} + +void RouteOrch::decNhgRefCount(const std::string &nhg_index) +{ + +} + +void RouteOrch::publishRouteState(const RouteBulkContext& ctx, const ReturnCode& status) +{ + +} diff --git a/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp b/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp index 2ba915d9c0..da3ae3578b 100644 --- a/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp +++ b/orchagent/p4orch/tests/gre_tunnel_manager_test.cpp @@ -4,11 +4,11 @@ #include #include +#include #include #include #include "ipaddress.h" -#include #include "mock_response_publisher.h" #include "mock_sai_router_interface.h" #include "mock_sai_serialize.h" diff --git a/orchagent/p4orch/tests/l3_admit_manager_test.cpp b/orchagent/p4orch/tests/l3_admit_manager_test.cpp index 33f88d1839..0fa5cb7ac3 100644 --- a/orchagent/p4orch/tests/l3_admit_manager_test.cpp +++ b/orchagent/p4orch/tests/l3_admit_manager_test.cpp @@ -4,10 +4,10 @@ #include #include +#include #include #include -#include #include "mock_response_publisher.h" #include "mock_sai_my_mac.h" #include "p4oidmapper.h" diff --git a/orchagent/p4orch/tests/mirror_session_manager_test.cpp b/orchagent/p4orch/tests/mirror_session_manager_test.cpp index e137681930..1361fc96b3 100644 --- a/orchagent/p4orch/tests/mirror_session_manager_test.cpp +++ b/orchagent/p4orch/tests/mirror_session_manager_test.cpp @@ -3,10 +3,10 @@ #include #include +#include #include #include -#include #include "mock_response_publisher.h" #include "mock_sai_mirror.h" #include "p4oidmapper.h" diff --git a/orchagent/p4orch/tests/neighbor_manager_test.cpp b/orchagent/p4orch/tests/neighbor_manager_test.cpp index f335c8295f..4db1db873e 100644 --- a/orchagent/p4orch/tests/neighbor_manager_test.cpp +++ b/orchagent/p4orch/tests/neighbor_manager_test.cpp @@ -3,10 +3,10 @@ #include #include +#include #include #include -#include #include "mock_response_publisher.h" #include "mock_sai_neighbor.h" #include "p4orch.h" diff --git a/orchagent/p4orch/tests/next_hop_manager_test.cpp b/orchagent/p4orch/tests/next_hop_manager_test.cpp index 620474f1a1..7a2e714bbc 100644 --- a/orchagent/p4orch/tests/next_hop_manager_test.cpp +++ b/orchagent/p4orch/tests/next_hop_manager_test.cpp @@ -4,11 +4,11 @@ #include #include +#include #include #include #include "ipaddress.h" -#include #include "mock_response_publisher.h" #include "mock_sai_hostif.h" #include "mock_sai_next_hop.h" diff --git a/orchagent/p4orch/tests/route_manager_test.cpp b/orchagent/p4orch/tests/route_manager_test.cpp index bd03f357cf..6229f69c36 100644 --- a/orchagent/p4orch/tests/route_manager_test.cpp +++ b/orchagent/p4orch/tests/route_manager_test.cpp @@ -5,11 +5,11 @@ #include #include +#include #include #include #include "ipprefix.h" -#include #include "mock_response_publisher.h" #include "mock_sai_route.h" #include "p4orch.h" diff --git a/orchagent/p4orch/tests/test_main.cpp b/orchagent/p4orch/tests/test_main.cpp index 787e0622f4..9bf1aea3c2 100644 --- a/orchagent/p4orch/tests/test_main.cpp +++ b/orchagent/p4orch/tests/test_main.cpp @@ -15,6 +15,7 @@ extern "C" #include "mock_sai_virtual_router.h" #include "p4orch.h" #include "portsorch.h" +#include "routeorch.h" #include "sai_serialize.h" #include "switchorch.h" #include "vrforch.h" @@ -47,6 +48,7 @@ PortsOrch *gPortsOrch; CrmOrch *gCrmOrch; P4Orch *gP4Orch; VRFOrch *gVrfOrch; +RouteOrch *gRouteOrch; FlowCounterRouteOrch *gFlowCounterRouteOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; @@ -55,12 +57,14 @@ swss::DBConnector *gStateDb; swss::DBConnector *gConfigDb; swss::DBConnector *gCountersDb; MacAddress gVxlanMacAddress; +MacAddress gMacAddress; sai_router_interface_api_t *sai_router_intfs_api; sai_neighbor_api_t *sai_neighbor_api; sai_next_hop_api_t *sai_next_hop_api; sai_next_hop_group_api_t *sai_next_hop_group_api; sai_route_api_t *sai_route_api; +sai_mpls_api_t *sai_mpls_api; sai_acl_api_t *sai_acl_api; sai_policer_api_t *sai_policer_api; sai_virtual_router_api_t *sai_virtual_router_api; @@ -99,7 +103,6 @@ bool parseHandleSaiStatusFailure(task_process_status status) return true; } - namespace { @@ -173,7 +176,7 @@ void AddVrf() } // namespace int main(int argc, char *argv[]) -{ +{ gBatchSize = DEFAULT_BATCH_SIZE; testing::InitGoogleTest(&argc, argv); @@ -182,6 +185,7 @@ int main(int argc, char *argv[]) sai_next_hop_api_t next_hop_api; sai_next_hop_group_api_t next_hop_group_api; sai_route_api_t route_api; + sai_mpls_api_t mpls_api; sai_acl_api_t acl_api; sai_policer_api_t policer_api; sai_virtual_router_api_t virtual_router_api; @@ -199,6 +203,7 @@ int main(int argc, char *argv[]) sai_next_hop_api = &next_hop_api; sai_next_hop_group_api = &next_hop_group_api; sai_route_api = &route_api; + sai_mpls_api = &mpls_api; sai_acl_api = &acl_api; sai_policer_api = &policer_api; sai_virtual_router_api = &virtual_router_api; @@ -230,6 +235,24 @@ int main(int argc, char *argv[]) gVrfOrch = &vrf_orch; gDirectory.set(static_cast(&vrf_orch)); + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + RouteOrch route_orch(gAppDb, route_tables, NULL, NULL, NULL, NULL, NULL, NULL); + gRouteOrch = &route_orch; + gDirectory.set(static_cast(&route_orch)); + + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + RouteOrch route_orch(gAppDb, route_tables, NULL, NULL, NULL, NULL, NULL, NULL); + gRouteOrch = &route_orch; + gDirectory.set(static_cast(&route_orch)); + FlowCounterRouteOrch flow_counter_route_orch(gConfigDb, std::vector{}); gFlowCounterRouteOrch = &flow_counter_route_orch; gDirectory.set(static_cast(&flow_counter_route_orch)); diff --git a/orchagent/p4orch/tests/wcmp_manager_test.cpp b/orchagent/p4orch/tests/wcmp_manager_test.cpp index c3aaeb6217..088264bba4 100644 --- a/orchagent/p4orch/tests/wcmp_manager_test.cpp +++ b/orchagent/p4orch/tests/wcmp_manager_test.cpp @@ -3,9 +3,9 @@ #include #include +#include #include -#include #include "mock_response_publisher.h" #include "mock_sai_acl.h" #include "mock_sai_hostif.h" diff --git a/orchagent/p4orch/wcmp_manager.cpp b/orchagent/p4orch/wcmp_manager.cpp index 67d87f1373..81c373b16f 100644 --- a/orchagent/p4orch/wcmp_manager.cpp +++ b/orchagent/p4orch/wcmp_manager.cpp @@ -1,5 +1,6 @@ #include "p4orch/wcmp_manager.h" +#include #include #include #include @@ -7,7 +8,6 @@ #include "SaiAttributeList.h" #include "crmorch.h" #include "dbconnector.h" -#include #include "logger.h" #include "p4orch/p4orch_util.h" #include "portsorch.h" @@ -734,13 +734,14 @@ void WcmpManager::updatePortOperStatusMap(const std::string &port, const sai_por port_oper_status_map[port] = status; } -ReturnCode WcmpManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) +ReturnCode WcmpManager::getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) { - std::string value; + std::string value; try { - nlohmann::json j = nlohmann::json::parse(json_key); + nlohmann::json j = nlohmann::json::parse(json_key); if (j.find(prependMatchField(p4orch::kWcmpGroupId)) != j.end()) { value = j.at(prependMatchField(p4orch::kWcmpGroupId)).get(); diff --git a/orchagent/p4orch/wcmp_manager.h b/orchagent/p4orch/wcmp_manager.h index 64fd4283e4..7d533bf28f 100644 --- a/orchagent/p4orch/wcmp_manager.h +++ b/orchagent/p4orch/wcmp_manager.h @@ -72,7 +72,8 @@ class WcmpManager : public ObjectManagerInterface void enqueue(const std::string &table_name, const swss::KeyOpFieldsValuesTuple &entry) override; void drain() override; std::string verifyState(const std::string &key, const std::vector &tuple) override; - ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, std::string &object_key) override; + ReturnCode getSaiObject(const std::string &json_key, sai_object_type_t &object_type, + std::string &object_key) override; // Prunes next hop members egressing through the given port. void pruneNextHops(const std::string &port); diff --git a/orchagent/port.h b/orchagent/port.h index f6b598edeb..dc8241ce3a 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -97,7 +97,8 @@ class Port Ext, // external Int, // internal Inb, // inband - Rec // recirculation + Rec, // recirculation + Dpc // DPU Connect Port on SmartSwitch }; public: @@ -164,6 +165,7 @@ class Port uint32_t m_nat_zone_id = 0; uint32_t m_vnid = VNID_NONE; uint32_t m_fdb_count = 0; + uint64_t m_flap_count = 0; uint32_t m_up_member_count = 0; uint32_t m_maximum_headroom = 0; std::set m_adv_speeds; diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp index 64c05b2aec..419cb7ff84 100644 --- a/orchagent/port/porthlpr.cpp +++ b/orchagent/port/porthlpr.cpp @@ -114,7 +114,8 @@ static const std::unordered_map portRoleMap = { PORT_ROLE_EXT, Port::Role::Ext }, { PORT_ROLE_INT, Port::Role::Int }, { PORT_ROLE_INB, Port::Role::Inb }, - { PORT_ROLE_REC, Port::Role::Rec } + { PORT_ROLE_REC, Port::Role::Rec }, + { PORT_ROLE_DPC, Port::Role::Dpc } }; // functions ---------------------------------------------------------------------------------------------------------- @@ -1001,7 +1002,7 @@ bool PortHelper::parsePortConfig(PortConfig &port) const } } - return this->validatePortConfig(port); + return true; } bool PortHelper::validatePortConfig(PortConfig &port) const diff --git a/orchagent/port/porthlpr.h b/orchagent/port/porthlpr.h index 4bcae7fca5..6729a83a4d 100644 --- a/orchagent/port/porthlpr.h +++ b/orchagent/port/porthlpr.h @@ -28,6 +28,7 @@ class PortHelper final std::string getAdminStatusStr(const PortConfig &port) const; bool parsePortConfig(PortConfig &port) const; + bool validatePortConfig(PortConfig &port) const; private: std::string getFieldValueStr(const PortConfig &port, const std::string &field) const; @@ -52,6 +53,4 @@ class PortHelper final bool parsePortRole(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortAdminStatus(PortConfig &port, const std::string &field, const std::string &value) const; bool parsePortDescription(PortConfig &port, const std::string &field, const std::string &value) const; - - bool validatePortConfig(PortConfig &port) const; }; diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h index 56b2541c37..5c4ad0d542 100644 --- a/orchagent/port/portschema.h +++ b/orchagent/port/portschema.h @@ -51,6 +51,7 @@ #define PORT_ROLE_INT "Int" #define PORT_ROLE_INB "Inb" #define PORT_ROLE_REC "Rec" +#define PORT_ROLE_DPC "Dpc" #define PORT_ALIAS "alias" #define PORT_INDEX "index" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 0ce38850d0..50c23d4aec 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -6,6 +6,7 @@ #include "vxlanorch.h" #include "directory.h" #include "subintf.h" +#include "notifications.h" #include #include @@ -581,6 +582,17 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vectorset_switch_attribute(gSwitchId, &attr) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("PortsOrch failed to set SAI_SWITCH_ATTR_PORT_HOST_TX_READY_NOTIFY attribute"); + } + Orch::addExecutor(new Consumer(new SubscriberStateTable(stateDb, STATE_TRANSCEIVER_INFO_TABLE_NAME, TableConsumable::DEFAULT_POP_BATCH_SIZE, 0), this, STATE_TRANSCEIVER_INFO_TABLE_NAME)); } @@ -1156,6 +1168,11 @@ map& PortsOrch::getAllPorts() return m_portList; } +unordered_set& PortsOrch::getAllVlans() +{ + return m_vlanPorts; +} + bool PortsOrch::getPort(string alias, Port &p) { SWSS_LOG_ENTER(); @@ -3090,6 +3107,30 @@ bool PortsOrch::removeVlanHostIntf(Port vl) return true; } +void PortsOrch::updateDbPortFlapCount(Port& port, sai_port_oper_status_t pstatus) +{ + SWSS_LOG_ENTER(); + + ++port.m_flap_count; + vector tuples; + FieldValueTuple tuple("flap_count", std::to_string(port.m_flap_count)); + tuples.push_back(tuple); + + auto now = std::chrono::system_clock::now(); + std::time_t now_c = std::chrono::system_clock::to_time_t(now); + if (pstatus == SAI_PORT_OPER_STATUS_DOWN) + { + FieldValueTuple tuple("last_down_time", std::ctime(&now_c)); + tuples.push_back(tuple); + } + else if (pstatus == SAI_PORT_OPER_STATUS_UP) + { + FieldValueTuple tuple("last_up_time", std::ctime(&now_c)); + tuples.push_back(tuple); + } + m_portTable->set(port.m_alias, tuples); +} + void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const { SWSS_LOG_ENTER(); @@ -3350,6 +3391,7 @@ bool PortsOrch::bake() addExistingData(APP_LAG_MEMBER_TABLE_NAME); addExistingData(APP_VLAN_TABLE_NAME); addExistingData(APP_VLAN_MEMBER_TABLE_NAME); + addExistingData(STATE_TRANSCEIVER_INFO_TABLE_NAME); return true; } @@ -3531,28 +3573,59 @@ void PortsOrch::doPortTask(Consumer &consumer) if (op == SET_COMMAND) { - auto &fvMap = m_portConfigMap[key]; - - for (const auto &cit : kfvFieldsValues(keyOpFieldsValues)) + auto parsePortFvs = [&](auto& fvMap) -> bool { - auto fieldName = fvField(cit); - auto fieldValue = fvValue(cit); + for (const auto &cit : kfvFieldsValues(keyOpFieldsValues)) + { + auto fieldName = fvField(cit); + auto fieldValue = fvValue(cit); - SWSS_LOG_INFO("FIELD: %s, VALUE: %s", fieldName.c_str(), fieldValue.c_str()); + SWSS_LOG_INFO("FIELD: %s, VALUE: %s", fieldName.c_str(), fieldValue.c_str()); - fvMap[fieldName] = fieldValue; - } + fvMap[fieldName] = fieldValue; + } + + pCfg.fieldValueMap = fvMap; + + if (!m_portHlpr.parsePortConfig(pCfg)) + { + return false; + } - pCfg.fieldValueMap = fvMap; + return true; + }; - if (!m_portHlpr.parsePortConfig(pCfg)) + if (m_portList.find(key) == m_portList.end()) { - it = taskMap.erase(it); - continue; + // Aggregate configuration while the port is not created. + auto &fvMap = m_portConfigMap[key]; + + if (!parsePortFvs(fvMap)) + { + it = taskMap.erase(it); + continue; + } + + if (!m_portHlpr.validatePortConfig(pCfg)) + { + it = taskMap.erase(it); + continue; + } + + /* Collect information about all received ports */ + m_lanesAliasSpeedMap[pCfg.lanes.value] = pCfg; } + else + { + // Port is already created, gather updated field-values. + std::unordered_map fvMap; - /* Collect information about all received ports */ - m_lanesAliasSpeedMap[pCfg.lanes.value] = pCfg; + if (!parsePortFvs(fvMap)) + { + it = taskMap.erase(it); + continue; + } + } // TODO: // Fix the issue below @@ -3668,6 +3741,9 @@ void PortsOrch::doPortTask(Consumer &consumer) PortSerdesAttrMap_t serdes_attr; getPortSerdesAttr(serdes_attr, pCfg); + // Saved configured admin status + bool admin_status = p.m_admin_state_up; + if (pCfg.autoneg.is_set) { if (!p.m_an_cfg || p.m_autoneg != pCfg.autoneg.value) @@ -4228,6 +4304,13 @@ void PortsOrch::doPortTask(Consumer &consumer) /* create host_tx_ready field in state-db */ initHostTxReadyState(p); + // Restore admin status if the port was brought down + if (admin_status != p.m_admin_state_up) + { + pCfg.admin_status.is_set = true; + pCfg.admin_status.value = admin_status; + } + /* Last step set port admin status */ if (pCfg.admin_status.is_set) { @@ -5298,7 +5381,7 @@ bool PortsOrch::initializePort(Port &port) /* Check warm start states */ vector tuples; bool exist = m_portTable->get(port.m_alias, tuples); - string operStatus; + string operStatus, flapCount = "0"; if (exist) { for (auto i : tuples) @@ -5307,9 +5390,14 @@ bool PortsOrch::initializePort(Port &port) { operStatus = fvValue(i); } + + if (fvField(i) == "flap_count") + { + flapCount = fvValue(i); + } } } - SWSS_LOG_DEBUG("initializePort %s with oper %s", port.m_alias.c_str(), operStatus.c_str()); + SWSS_LOG_INFO("Port %s with oper %s flap_count=%s", port.m_alias.c_str(), operStatus.c_str(), flapCount.c_str()); /** * Create database port oper status as DOWN if attr missing @@ -5330,6 +5418,20 @@ bool PortsOrch::initializePort(Port &port) port.m_oper_status = SAI_PORT_OPER_STATUS_DOWN; } + // initalize port flap count + if (!flapCount.empty()) + { + try + { + port.m_flap_count = stoull(flapCount); + m_portTable->hset(port.m_alias, "flap_count", flapCount); + } + catch (const std::exception &e) + { + SWSS_LOG_ERROR("Failed to get port (%s) flap_count: %s", port.m_alias.c_str(), e.what()); + } + } + /* initialize port admin status */ if (!getPortAdminStatus(port.m_port_id, port.m_admin_state_up)) { @@ -5743,6 +5845,7 @@ bool PortsOrch::addVlan(string vlan_alias) m_portList[vlan_alias] = vlan; m_port_ref_count[vlan_alias] = 0; saiOidToAlias[vlan_oid] = vlan_alias; + m_vlanPorts.emplace(vlan_alias); return true; } @@ -5809,6 +5912,7 @@ bool PortsOrch::removeVlan(Port vlan) saiOidToAlias.erase(vlan.m_vlan_info.vlan_oid); m_portList.erase(vlan.m_alias); m_port_ref_count.erase(vlan.m_alias); + m_vlanPorts.erase(vlan.m_alias); return true; } @@ -7561,6 +7665,7 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) if (port.m_type == Port::PHY) { updateDbPortOperStatus(port, status); + updateDbPortFlapCount(port, status); updateGearboxPortOperStatus(port); /* Refresh the port states and reschedule the poller tasks */ @@ -7675,6 +7780,18 @@ void PortsOrch::refreshPortStatus() { updateDbPortOperSpeed(port, 0); } + sai_port_fec_mode_t fec_mode; + string fec_str = "N/A"; + if (oper_fec_sup && getPortOperFec(port, fec_mode)) + { + if (!m_portHlpr.fecToStr(fec_str, fec_mode)) + { + SWSS_LOG_ERROR("Error unknown fec mode %d while querying port %s fec mode", + static_cast(fec_mode), port.m_alias.c_str()); + fec_str = "N/A"; + } + } + updateDbPortOperFec(port,fec_str); } } } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 4d069ccfc5..21ed299681 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -2,6 +2,7 @@ #define SWSS_PORTSORCH_H #include +#include #include "acltable.h" #include "orch.h" @@ -146,10 +147,13 @@ class PortsOrch : public Orch, public Subject bool setHostIntfsOperStatus(const Port& port, bool up) const; void updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const; + void updateDbPortFlapCount(Port& port, sai_port_oper_status_t pstatus); bool createVlanHostIntf(Port& vl, string hostif_name); bool removeVlanHostIntf(Port vl); + unordered_set& getAllVlans(); + bool createBindAclTableGroup(sai_object_id_t port_oid, sai_object_id_t acl_table_oid, sai_object_id_t &group_oid, @@ -306,6 +310,7 @@ class PortsOrch : public Orch, public Subject map m_gearboxPortMap; map> m_gearboxPortListLaneMap; + unordered_set m_vlanPorts; port_config_state_t m_portConfigState = PORT_CONFIG_MISSING; sai_uint32_t m_portCount; map, sai_object_id_t> m_portListLaneMap; diff --git a/orchagent/response_publisher.cpp b/orchagent/response_publisher.cpp index 031f1aefef..d5b94a586d 100644 --- a/orchagent/response_publisher.cpp +++ b/orchagent/response_publisher.cpp @@ -50,7 +50,7 @@ void RecordResponse(const std::string &response_channel, const std::string &key, { if (!swss::Recorder::Instance().respub.isRecord()) { - return; + return; } std::string s = response_channel + ":" + key + "|" + status; @@ -64,10 +64,9 @@ void RecordResponse(const std::string &response_channel, const std::string &key, } // namespace -ResponsePublisher::ResponsePublisher(bool buffered) : - m_db(std::make_unique("APPL_STATE_DB", 0)), - m_pipe(std::make_unique(m_db.get())), - m_buffered(buffered) +ResponsePublisher::ResponsePublisher(bool buffered) + : m_db(std::make_unique("APPL_STATE_DB", 0)), + m_pipe(std::make_unique(m_db.get())), m_buffered(buffered) { } diff --git a/orchagent/response_publisher.h b/orchagent/response_publisher.h index ff7bd291e4..985532e827 100644 --- a/orchagent/response_publisher.h +++ b/orchagent/response_publisher.h @@ -7,9 +7,9 @@ #include "dbconnector.h" #include "notificationproducer.h" +#include "recorder.h" #include "response_publisher_interface.h" #include "table.h" -#include "recorder.h" // This class performs two tasks when publish is called: // 1. Sends a notification into the redis channel. @@ -46,14 +46,14 @@ class ResponsePublisher : public ResponsePublisherInterface /** * @brief Flush pending responses - */ + */ void flush(); /** * @brief Set buffering mode * * @param buffered Flag whether responses are buffered - */ + */ void setBuffered(bool buffered); private: diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 041a988d5c..1a71639504 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -139,7 +139,7 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, * Hence add a single /128 route entry for the link-local interface * address pointing to the CPU port. */ - IpPrefix linklocal_prefix = getLinkLocalEui64Addr(); + IpPrefix linklocal_prefix = getLinkLocalEui64Addr(gMacAddress); addLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); @@ -152,12 +152,12 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", default_link_local_prefix.to_string().c_str()); } -std::string RouteOrch::getLinkLocalEui64Addr(void) +std::string RouteOrch::getLinkLocalEui64Addr(const MacAddress &mac) { SWSS_LOG_ENTER(); string ip_prefix; - const uint8_t *gmac = gMacAddress.getMac(); + const uint8_t *gmac = mac.getMac(); uint8_t eui64_interface_id[EUI64_INTF_ID_LEN]; char ipv6_ll_addr[INET6_ADDRSTRLEN] = {0}; @@ -807,6 +807,18 @@ void RouteOrch::doTask(Consumer& consumer) } else { + if(ipv.size() != rmacv.size()){ + SWSS_LOG_ERROR("Skip route %s, it has an invalid router mac field %s", key.c_str(), remote_macs.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if(ipv.size() != vni_labelv.size()){ + SWSS_LOG_ERROR("Skip route %s, it has an invalid vni label field %s", key.c_str(), vni_labels.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + for (uint32_t i = 0; i < ipv.size(); i++) { if (i) nhg_str += NHG_DELIMITER; diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index b232137766..0974fd2b53 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -217,7 +217,7 @@ class RouteOrch : public Orch, public Subject void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); void delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); - std::string getLinkLocalEui64Addr(void); + std::string getLinkLocalEui64Addr(const MacAddress &mac); unsigned int getNhgCount() { return m_nextHopGroupCount; } unsigned int getMaxNhgCount() { return m_maxNextHopGroupCount; } diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 6fcf4c5014..d731b7b8ac 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -82,6 +82,7 @@ sai_dash_inbound_routing_api_t* sai_dash_inbound_routing_api; sai_dash_eni_api_t* sai_dash_eni_api; sai_dash_vip_api_t* sai_dash_vip_api; sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; +sai_twamp_api_t* sai_twamp_api; extern sai_object_id_t gSwitchId; @@ -217,6 +218,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_DASH_ENI, (void**)&sai_dash_eni_api); sai_api_query((sai_api_t)SAI_API_DASH_VIP, (void**)&sai_dash_vip_api); sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); + sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -256,6 +258,7 @@ void initSaiApi() sai_log_set(SAI_API_BFD, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_MY_MAC, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_GENERIC_PROGRAMMABLE, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_TWAMP, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis() diff --git a/orchagent/twamporch.cpp b/orchagent/twamporch.cpp new file mode 100644 index 0000000000..cd4fe0b666 --- /dev/null +++ b/orchagent/twamporch.cpp @@ -0,0 +1,1053 @@ +#include "twamporch.h" +#include "vrforch.h" +#include "crmorch.h" +#include "logger.h" +#include "swssnet.h" +#include "converter.h" +#include "sai_serialize.h" +#include "tokenize.h" +#include "notifier.h" +#include "notifications.h" + +#include + +using namespace std; +using namespace swss; + +/* TWAMP infor */ +#define TWAMP_SESSION_MODE "MODE" +#define TWAMP_SESSION_ROLE "ROLE" +#define TWAMP_SESSION_VRF_NAME "VRF_NAME" +#define TWAMP_SESSION_HW_LOOKUP "HW_LOOKUP" + +/* TWAMP-test packet */ +#define TWAMP_SESSION_SRC_IP "SRC_IP" +#define TWAMP_SESSION_SRC_UDP_PORT "SRC_UDP_PORT" +#define TWAMP_SESSION_DST_IP "DST_IP" +#define TWAMP_SESSION_DST_UDP_PORT "DST_UDP_PORT" +#define TWAMP_SESSION_DSCP "DSCP" +#define TWAMP_SESSION_TTL "TTL" +#define TWAMP_SESSION_PACKET_TIMESTAMP_FORMAT "TIMESTAMP_FORMAT" +#define TWAMP_SESSION_PACKET_PADDING_SIZE "PADDING_SIZE" + +/* Session-Sender */ +#define TWAMP_SESSION_TX_PACKET_COUNT "PACKET_COUNT" +#define TWAMP_SESSION_TX_MONITOR_TIME "MONITOR_TIME" +#define TWAMP_SESSION_TX_INTERVAL "TX_INTERVAL" +#define TWAMP_SESSION_TIMEOUT "TIMEOUT" +#define TWAMP_SESSION_STATISTICS_INTERVAL "STATISTICS_INTERVAL" +#define TWAMP_SESSION_ADMIN_STATE "ADMIN_STATE" + +/* TWAMP session status */ +#define TWAMP_SESSION_STATUS "status" +#define TWAMP_SESSION_STATUS_ACTIVE "active" +#define TWAMP_SESSION_STATUS_INACTIVE "inactive" + +#define TWAMP_SESSION_TX_MODE_PACKET_NUM "packet_num" +#define TWAMP_SESSION_TX_MODE_CONTINUOUS "continuous" + +#define TWAMP_SESSION_DSCP_MIN 0 +#define TWAMP_SESSION_DSCP_MAX 63 + +#define TWAMP_SESSION_TIMEOUT_MIN 1 +#define TWAMP_SESSION_TIMEOUT_MAX 10 + +static map twamp_role_map = +{ + { "SENDER", SAI_TWAMP_SESSION_ROLE_SENDER }, + { "REFLECTOR", SAI_TWAMP_SESSION_ROLE_REFLECTOR } +}; + +static map twamp_mode_map = +{ + { "FULL", SAI_TWAMP_MODE_FULL }, + { "LIGHT", SAI_TWAMP_MODE_LIGHT } +}; + +static map timestamp_format_map = +{ + { "NTP", SAI_TWAMP_TIMESTAMP_FORMAT_NTP }, + { "PTP", SAI_TWAMP_TIMESTAMP_FORMAT_PTP } +}; + +static map session_admin_state_map = +{ + { "ENABLED", true }, + { "DISABLED", false } +}; + +static map hw_lookup_map = +{ + { "TRUE", true }, + { "FALSE", false } +}; + +/* Global variables */ +extern sai_object_id_t gSwitchId; +extern sai_object_id_t gVirtualRouterId; +extern sai_switch_api_t *sai_switch_api; +extern sai_twamp_api_t *sai_twamp_api; +extern CrmOrch *gCrmOrch; + +const vector twamp_session_stat_ids = +{ + SAI_TWAMP_SESSION_STAT_RX_PACKETS, + SAI_TWAMP_SESSION_STAT_RX_BYTE, + SAI_TWAMP_SESSION_STAT_TX_PACKETS, + SAI_TWAMP_SESSION_STAT_TX_BYTE, + SAI_TWAMP_SESSION_STAT_DROP_PACKETS, + SAI_TWAMP_SESSION_STAT_MAX_LATENCY, + SAI_TWAMP_SESSION_STAT_MIN_LATENCY, + SAI_TWAMP_SESSION_STAT_AVG_LATENCY, + SAI_TWAMP_SESSION_STAT_MAX_JITTER, + SAI_TWAMP_SESSION_STAT_MIN_JITTER, + SAI_TWAMP_SESSION_STAT_AVG_JITTER +}; + + + +TwampOrch::TwampOrch(TableConnector confDbConnector, TableConnector stateDbConnector, SwitchOrch *switchOrch, PortsOrch *portOrch, VRFOrch *vrfOrch) : + Orch(confDbConnector.first, confDbConnector.second), + m_stateDbTwampTable(stateDbConnector.first, stateDbConnector.second), + m_switchOrch(switchOrch), + m_portsOrch(portOrch), + m_vrfOrch(vrfOrch) +{ + /* Set entries count to 0 */ + m_maxTwampSessionCount = m_twampSessionCount = 0; + + /* Get the Maximum supported TWAMP sessions */ + SWSS_LOG_INFO("Get the Maximum supported TWAMP sessions"); + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_MAX_TWAMP_SESSION; + sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Twamp session resource availability is not supported. Skipping ..."); + return; + } + else + { + m_maxTwampSessionCount = attr.value.u32; + } + + /* Set MAX entries to state DB */ + if (m_maxTwampSessionCount) + { + vector fvTuple; + fvTuple.emplace_back("MAX_TWAMP_SESSION_COUNT", to_string(m_maxTwampSessionCount)); + m_switchOrch->set_switch_capability(fvTuple); + } + else + { + SWSS_LOG_NOTICE("Twamp session resource availability is not supported. Skipping ..."); + return; + } + + /* Add TWAMP session event notification support */ + DBConnector *notificationsDb = new DBConnector("ASIC_DB", 0); + m_twampNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); + auto twampNotifier = new Notifier(m_twampNotificationConsumer, this, "TWAMP_NOTIFICATIONS"); + Orch::addExecutor(twampNotifier); + register_event_notif = false; + + /* Initialize DB connectors */ + m_asicDb = shared_ptr(new DBConnector("ASIC_DB", 0)); + m_countersDb = shared_ptr(new DBConnector("COUNTERS_DB", 0)); + + /* Initialize VIDTORID table */ + m_vidToRidTable = unique_ptr
(new Table(m_asicDb.get(), "VIDTORID")); + + /* Initialize counter tables */ + m_counterTwampSessionNameMapTable = unique_ptr
(new Table(m_countersDb.get(), COUNTERS_TWAMP_SESSION_NAME_MAP)); + m_countersTable = unique_ptr
(new Table(m_countersDb.get(), COUNTERS_TABLE)); +} + +bool TwampOrch::isSessionExists(const string& name) +{ + SWSS_LOG_ENTER(); + + return m_twampEntries.find(name) != m_twampEntries.end(); +} + +bool TwampOrch::getSessionName(const sai_object_id_t oid, string& name) +{ + SWSS_LOG_ENTER(); + + for (const auto& it: m_twampEntries) + { + if (it.second.session_id == oid) + { + name = it.first; + return true; + } + } + + return false; +} + +bool TwampOrch::validateUdpPort(uint16_t udp_port) +{ + if (udp_port == 862) + { + return true; + } + if (udp_port == 863) + { + return true; + } + if (udp_port >= 1025) + { + return true; + } + return false; +} + +void TwampOrch::increaseTwampSessionCount(void) +{ + m_twampSessionCount++; +} + +void TwampOrch::decreaseTwampSessionCount(void) +{ + m_twampSessionCount--; +} + +bool TwampOrch::checkTwampSessionCount(void) +{ + return m_twampSessionCount < m_maxTwampSessionCount; +} + +void TwampOrch::setSessionStatus(const string& name, const string& status) +{ + SWSS_LOG_ENTER(); + + vector fvVector; + fvVector.emplace_back(TWAMP_SESSION_STATUS, status); + m_stateDbTwampTable.set(name, fvVector); +} + +bool TwampOrch::getSessionStatus(const string &name, string& status) +{ + SWSS_LOG_ENTER(); + + if (m_stateDbTwampTable.hget(name, TWAMP_SESSION_STATUS, status)) + { + return true; + } + return false; +} + +void TwampOrch::removeSessionStatus(const string& name) +{ + SWSS_LOG_ENTER(); + + m_stateDbTwampTable.del(name); +} + +void TwampOrch::removeSessionCounter(const sai_object_id_t session_id) +{ + SWSS_LOG_ENTER(); + + string key_pattern = "COUNTERS:" + sai_serialize_object_id(session_id) + "*"; + auto keys = m_countersDb->keys(key_pattern); + for (auto& k : keys) + { + m_countersDb->del(k); + } +} + +void TwampOrch::initSessionStats(const string& name) +{ + SWSS_LOG_ENTER(); + + auto it = m_twampStatistics.find(name); + if (it == m_twampStatistics.end()) + { + SWSS_LOG_ERROR("Failed to init non-existent twamp session %s stat info", name.c_str()); + return; + } + + TwampStats& total_stats = it->second; + + total_stats.rx_packets = 0; + total_stats.rx_bytes = 0; + total_stats.tx_packets = 0; + total_stats.tx_bytes = 0; + total_stats.drop_packets = 0; + total_stats.max_latency = 0; + total_stats.min_latency = 0; + total_stats.avg_latency = 0; + total_stats.max_jitter = 0; + total_stats.min_jitter = 0; + total_stats.avg_jitter = 0; + total_stats.avg_latency_total = 0; + total_stats.avg_jitter_total = 0; +} + +bool TwampOrch::registerTwampEventNotification(void) +{ + sai_attribute_t attr; + sai_status_t status; + sai_attr_capability_t capability; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, + SAI_SWITCH_ATTR_TWAMP_SESSION_EVENT_NOTIFY, + &capability); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Unable to query the TWAMP event notification capability"); + return false; + } + + if (!capability.set_implemented) + { + SWSS_LOG_NOTICE("TWAMP register event notification not supported"); + return false; + } + + attr.id = SAI_SWITCH_ATTR_TWAMP_SESSION_EVENT_NOTIFY; + attr.value.ptr = (void *)on_twamp_session_event; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to register TWAMP notification handler"); + return false; + } + + return true; +} + +bool TwampOrch::activateSession(const string& name, TwampEntry& entry) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_TWAMP_SESSION_ATTR_TWAMP_MODE; + attr.value.s32 = entry.mode; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_SESSION_ROLE; + attr.value.s32 = entry.role; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_HW_LOOKUP_VALID; + attr.value.booldata = entry.hw_lookup; + attrs.emplace_back(attr); + + if (entry.vrf_id) + { + attr.id = SAI_TWAMP_SESSION_ATTR_VIRTUAL_ROUTER; + attr.value.oid = entry.vrf_id; + attrs.emplace_back(attr); + } + + attr.id = SAI_TWAMP_SESSION_ATTR_SRC_IP; + copy(attr.value.ipaddr, entry.src_ip); + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_DST_IP; + copy(attr.value.ipaddr, entry.dst_ip); + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_UDP_SRC_PORT; + attr.value.u32 = entry.src_udp_port; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_UDP_DST_PORT; + attr.value.u32 = entry.dst_udp_port; + attrs.emplace_back(attr); + + if (entry.role == SAI_TWAMP_SESSION_ROLE_SENDER) + { + if (entry.tx_mode == TWAMP_SESSION_TX_MODE_PACKET_NUM) + { + attr.id = SAI_TWAMP_SESSION_ATTR_TWAMP_PKT_TX_MODE; + attr.value.s32 = SAI_TWAMP_PKT_TX_MODE_PACKET_COUNT; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_TX_PKT_CNT; + attr.value.u32 = entry.packet_count; + attrs.emplace_back(attr); + } + else if (entry.tx_mode == TWAMP_SESSION_TX_MODE_CONTINUOUS) + { + if (entry.monitor_time) + { + attr.id = SAI_TWAMP_SESSION_ATTR_TWAMP_PKT_TX_MODE; + attr.value.u32 = SAI_TWAMP_PKT_TX_MODE_PERIOD; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_TX_PKT_PERIOD; + attr.value.u32 = entry.monitor_time; + attrs.emplace_back(attr); + } + else + { + attr.id = SAI_TWAMP_SESSION_ATTR_TWAMP_PKT_TX_MODE; + attr.value.u32 = SAI_TWAMP_PKT_TX_MODE_CONTINUOUS; + attrs.emplace_back(attr); + } + } + + attr.id = SAI_TWAMP_SESSION_ATTR_TX_INTERVAL; + attr.value.u32 = entry.tx_interval; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_TIMEOUT; + attr.value.u32 = entry.timeout; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_STATISTICS_INTERVAL; + attr.value.u32 = entry.statistics_interval; + attrs.emplace_back(attr); + + attr.id = SAI_TWAMP_SESSION_ATTR_SESSION_ENABLE_TRANSMIT; + attr.value.booldata = entry.admin_state; + attrs.emplace_back(attr); + } + + setSessionStatus(name, TWAMP_SESSION_STATUS_INACTIVE); + + status = sai_twamp_api->create_twamp_session(&entry.session_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create twamp session %s, status %d", name.c_str(), status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_TWAMP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + /* increase VRF reference count */ + m_vrfOrch->increaseVrfRefCount(entry.vrf_id); + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_TWAMP_ENTRY); + + increaseTwampSessionCount(); + + if (entry.role == SAI_TWAMP_SESSION_ROLE_REFLECTOR) + { + setSessionStatus(name, TWAMP_SESSION_STATUS_ACTIVE); + } + + return true; +} + +bool TwampOrch::deactivateSession(const string& name, TwampEntry& entry) +{ + SWSS_LOG_ENTER(); + sai_status_t status; + + status = sai_twamp_api->remove_twamp_session(entry.session_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove twamp session %s, status %d", name.c_str(), status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_TWAMP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + /* decrease VRF reference count */ + m_vrfOrch->decreaseVrfRefCount(entry.vrf_id); + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_TWAMP_ENTRY); + + decreaseTwampSessionCount(); + + setSessionStatus(name, TWAMP_SESSION_STATUS_INACTIVE); + + return true; +} + +bool TwampOrch::setSessionTransmitEn(TwampEntry& entry, string admin_state) +{ + SWSS_LOG_ENTER(); + + if (entry.role != SAI_TWAMP_SESSION_ROLE_SENDER) + { + return false; + } + + auto found = session_admin_state_map.find(admin_state); + if (found == session_admin_state_map.end()) + { + SWSS_LOG_ERROR("Incorrect transmit value: %s", admin_state.c_str()); + return false; + } + + sai_attribute_t attr; + attr.id = SAI_TWAMP_SESSION_ATTR_SESSION_ENABLE_TRANSMIT; + attr.value.booldata = found->second; + sai_status_t status = sai_twamp_api->set_twamp_session_attribute(entry.session_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set twamp session %" PRIx64 " %s transmit, status %d", + entry.session_id, admin_state.c_str(), status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_TWAMP, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +task_process_status TwampOrch::createEntry(const string& key, const vector& data) +{ + SWSS_LOG_ENTER(); + + if (!register_event_notif) + { + if (!registerTwampEventNotification()) + { + SWSS_LOG_ERROR("TWAMP session for %s cannot be created", key.c_str()); + return task_process_status::task_failed; + } + register_event_notif = true; + } + + if (!checkTwampSessionCount()) + { + SWSS_LOG_NOTICE("Failed to create twamp session %s: resources are not available", key.c_str()); + return task_process_status::task_failed; + } + + TwampEntry entry; + for (auto i : data) + { + try { + string attr_name = to_upper(fvField(i)); + string attr_value = fvValue(i); + + if (attr_name == TWAMP_SESSION_MODE) + { + string value = to_upper(attr_value); + if (twamp_mode_map.find(value) == twamp_mode_map.end()) + { + SWSS_LOG_ERROR("Failed to parse valid mode %s", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.mode = twamp_mode_map[value]; + } + else if (attr_name == TWAMP_SESSION_ROLE) + { + string value = to_upper(attr_value); + if (twamp_role_map.find(value) == twamp_role_map.end()) + { + SWSS_LOG_ERROR("Failed to parse valid role %s", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.role = twamp_role_map[value]; + } + else if (attr_name == TWAMP_SESSION_SRC_IP) + { + entry.src_ip = attr_value; + } + else if (attr_name == TWAMP_SESSION_DST_IP) + { + entry.dst_ip = attr_value; + } + else if (attr_name == TWAMP_SESSION_SRC_UDP_PORT) + { + uint16_t value = to_uint(attr_value); + if (!validateUdpPort(value)) + { + SWSS_LOG_ERROR("Failed to parse valid souce udp port %d", value); + return task_process_status::task_invalid_entry; + } + entry.src_udp_port = value; + } + else if (attr_name == TWAMP_SESSION_DST_UDP_PORT) + { + uint16_t value = to_uint(attr_value); + if (!validateUdpPort(value)) + { + SWSS_LOG_ERROR("Failed to parse valid destination udp port %d", value); + return task_process_status::task_invalid_entry; + } + entry.dst_udp_port = to_uint(attr_value); + } + else if (attr_name == TWAMP_SESSION_VRF_NAME) + { + if (attr_value == "default") + { + entry.vrf_id = gVirtualRouterId; + } + else + { + if (!m_vrfOrch->isVRFexists(attr_value)) + { + SWSS_LOG_WARN("Vrf '%s' hasn't been created yet", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.vrf_id = m_vrfOrch->getVRFid(attr_value); + } + } + else if (attr_name == TWAMP_SESSION_DSCP) + { + entry.dscp = to_uint(attr_value, TWAMP_SESSION_DSCP_MIN, TWAMP_SESSION_DSCP_MAX); + } + else if (attr_name == TWAMP_SESSION_TTL) + { + entry.ttl = to_uint(attr_value); + } + else if (attr_name == TWAMP_SESSION_PACKET_TIMESTAMP_FORMAT) + { + string value = to_upper(attr_value); + if (timestamp_format_map.find(value) == timestamp_format_map.end()) + { + SWSS_LOG_ERROR("Failed to parse timestamp format value: %s", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.timestamp_format = timestamp_format_map[value]; + } + else if (attr_name == TWAMP_SESSION_PACKET_PADDING_SIZE) + { + entry.padding_size = to_uint(attr_value); + } + else if (attr_name == TWAMP_SESSION_TX_PACKET_COUNT) + { + if (entry.tx_mode == TWAMP_SESSION_TX_MODE_CONTINUOUS) + { + SWSS_LOG_ERROR("Configured packet count %s is conflict with monitor time", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + + entry.packet_count = to_uint(attr_value); + entry.tx_mode = TWAMP_SESSION_TX_MODE_PACKET_NUM; + } + else if (attr_name == TWAMP_SESSION_TX_MONITOR_TIME) + { + if (entry.tx_mode == TWAMP_SESSION_TX_MODE_PACKET_NUM) + { + SWSS_LOG_ERROR("Configured monitor time %s is conflict with packet count", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + + entry.monitor_time = to_uint(attr_value); + entry.tx_mode = TWAMP_SESSION_TX_MODE_CONTINUOUS; + } + else if (attr_name == TWAMP_SESSION_TX_INTERVAL) + { + entry.tx_interval = to_uint(attr_value); + } + else if (attr_name == TWAMP_SESSION_STATISTICS_INTERVAL) + { + entry.statistics_interval = to_uint(attr_value); + } + else if (attr_name == TWAMP_SESSION_TIMEOUT) + { + entry.timeout = to_uint(attr_value, TWAMP_SESSION_TIMEOUT_MIN, TWAMP_SESSION_TIMEOUT_MAX); + } + else if (attr_name == TWAMP_SESSION_ADMIN_STATE) + { + string value = to_upper(attr_value); + if (session_admin_state_map.find(value) == session_admin_state_map.end()) + { + SWSS_LOG_ERROR("Failed to parse transmit mode value: %s", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.admin_state = session_admin_state_map[value]; + } + else if (attr_name == TWAMP_SESSION_HW_LOOKUP) + { + string value = to_upper(attr_value); + if (hw_lookup_map.find(value) == hw_lookup_map.end()) + { + SWSS_LOG_ERROR("Failed to parse hw lookup value: %s", attr_value.c_str()); + return task_process_status::task_invalid_entry; + } + entry.hw_lookup = hw_lookup_map[value]; + } + else + { + SWSS_LOG_ERROR("Failed to parse session %s configuration. Unknown attribute %s", key.c_str(), attr_name.c_str()); + return task_process_status::task_invalid_entry; + } + } + catch (const exception& e) + { + SWSS_LOG_ERROR("Failed to parse session %s attribute %s error: %s.", key.c_str(), fvField(i).c_str(), e.what()); + return task_process_status::task_invalid_entry; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to parse session %s attribute %s. Unknown error has been occurred", key.c_str(), fvField(i).c_str()); + return task_process_status::task_failed; + } + } + + m_twampEntries.emplace(key, entry); + + if (entry.role == SAI_TWAMP_SESSION_ROLE_SENDER) + { + TwampStats hw_stats; + memset(&hw_stats, 0, sizeof(TwampStats)); + m_twampStatistics.emplace(key, hw_stats); + initSessionStats(key); + } + + auto &session = m_twampEntries.find(key)->second; + if (!activateSession(key, session)) + { + SWSS_LOG_ERROR("Failed to create twamp session %s", key.c_str()); + return task_process_status::task_failed; + } + + return task_process_status::task_success; +} + +task_process_status TwampOrch::updateEntry(const string& key, const vector& data) +{ + SWSS_LOG_ENTER(); + + auto it = m_twampEntries.find(key); + if (it == m_twampEntries.end()) + { + SWSS_LOG_NOTICE("Failed to set twamp session, session %s not exists", key.c_str()); + return task_process_status::task_invalid_entry; + } + TwampEntry& entry = it->second; + + for (auto i : data) + { + try { + const auto &attr_field = to_upper(fvField(i)); + const auto &attr_value = fvValue(i); + + if (attr_field == TWAMP_SESSION_ADMIN_STATE) + { + string value = to_upper(attr_value); + if (setSessionTransmitEn(entry, value)) + { + entry.admin_state = session_admin_state_map[value]; + if (entry.admin_state) + { + string running_status; + getSessionStatus(key, running_status); + if (running_status == TWAMP_SESSION_STATUS_INACTIVE) + { + removeSessionCounter(entry.session_id); + initSessionStats(key); + } + setSessionStatus(key, TWAMP_SESSION_STATUS_ACTIVE); + SWSS_LOG_NOTICE("Activated twamp session %s", key.c_str()); + } + else + { + setSessionStatus(key, TWAMP_SESSION_STATUS_INACTIVE); + SWSS_LOG_NOTICE("Deactivated twamp session %s", key.c_str()); + } + } + else + { + SWSS_LOG_ERROR("Failed to set twamp session %s transmit %s", key.c_str(), attr_value.c_str()); + } + } + else + { + SWSS_LOG_DEBUG("Ignore to parse session %s configuration attribute %s", key.c_str(), fvField(i).c_str()); + } + } + catch (const exception& e) + { + SWSS_LOG_ERROR("Failed to parse session %s attribute %s error: %s.", key.c_str(), fvField(i).c_str(), e.what()); + return task_process_status::task_invalid_entry; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to parse session %s attribute %s. Unknown error has been occurred", key.c_str(), fvField(i).c_str()); + return task_process_status::task_failed; + } + } + + return task_process_status::task_success; +} + +task_process_status TwampOrch::deleteEntry(const string& key) +{ + SWSS_LOG_ENTER(); + + auto it = m_twampEntries.find(key); + if (it == m_twampEntries.end()) + { + SWSS_LOG_ERROR("Failed to remove non-existent twamp session %s", key.c_str()); + return task_process_status::task_invalid_entry; + } + + TwampEntry& entry = it->second; + + if (!deactivateSession(key, entry)) + { + SWSS_LOG_ERROR("Failed to remove twamp session %s", key.c_str()); + return task_process_status::task_failed; + } + + /* remove TWAMP session in STATE_DB */ + removeSessionStatus(key); + + /* remove TWAMP session maps in COUNTERS_DB */ + m_counterTwampSessionNameMapTable->hdel("", key); + + /* remove TWAMP session in COUNTER_DB */ + removeSessionCounter(entry.session_id); + + /* remove soft table in orchagent */ + m_twampEntries.erase(key); + m_twampStatistics.erase(key); + + SWSS_LOG_NOTICE("Removed twamp session %s", key.c_str()); + + return task_process_status::task_success; +} + +void TwampOrch::doTask(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + + if (!m_portsOrch->allPortsReady()) + { + return; + } + + 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 data = kfvFieldsValues(t); + task_process_status task_status = task_process_status::task_failed; + + if (op == SET_COMMAND) + { + if (!isSessionExists(key)) + { + task_status = createEntry(key, data); + } + else + { + task_status = updateEntry(key, data); + } + } + else if (op == DEL_COMMAND) + { + task_status = deleteEntry(key); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + + /* Specifically retry the task when asked */ + if (task_status == task_process_status::task_need_retry) + { + it++; + } + else + { + it = consumer.m_toSync.erase(it); + } + } +} + +bool TwampOrch::addCounterNameMap(const string& name, const sai_object_id_t session_id) +{ + SWSS_LOG_ENTER(); + + string value; + const auto id = sai_serialize_object_id(session_id); + + if (m_vidToRidTable->hget("", id, value)) + { + vector fields; + fields.emplace_back(name, id); + m_counterTwampSessionNameMapTable->set("", fields); + + return true; + } + else + { + SWSS_LOG_NOTICE("TWAMP session counter %s already exists.", name.c_str()); + return true; + } + + return false; +} + +void TwampOrch::saveSessionStatsLatest(const sai_object_id_t session_id, const uint32_t index, const vector& stats) +{ + SWSS_LOG_ENTER(); + + vector values; + + for (const auto& it: twamp_session_stat_ids) + { + values.emplace_back(sai_serialize_twamp_session_stat(it), to_string(stats[it])); + } + + m_countersTable->set(sai_serialize_object_id(session_id) + ":INDEX:" + to_string(index), values); + + return; +} + +void TwampOrch::calculateCounters(const string& name, const uint32_t index, const vector& stats) +{ + SWSS_LOG_ENTER(); + + auto it = m_twampStatistics.find(name); + if (it == m_twampStatistics.end()) + { + SWSS_LOG_ERROR("Failed to caculate non-existent twamp session %s", name.c_str()); + return; + } + + TwampStats& total_stats = it->second; + /* packets */ + total_stats.rx_packets += stats[SAI_TWAMP_SESSION_STAT_RX_PACKETS]; + total_stats.rx_bytes += stats[SAI_TWAMP_SESSION_STAT_RX_BYTE]; + total_stats.tx_packets += stats[SAI_TWAMP_SESSION_STAT_TX_PACKETS]; + total_stats.tx_bytes += stats[SAI_TWAMP_SESSION_STAT_TX_BYTE]; + total_stats.drop_packets += stats[SAI_TWAMP_SESSION_STAT_DROP_PACKETS]; + + /* latency */ + total_stats.max_latency = (stats[SAI_TWAMP_SESSION_STAT_MAX_LATENCY] > total_stats.max_latency) ? + stats[SAI_TWAMP_SESSION_STAT_MAX_LATENCY] : total_stats.max_latency; + total_stats.min_latency = (index == 1) ? stats[SAI_TWAMP_SESSION_STAT_MIN_LATENCY] : + ((stats[SAI_TWAMP_SESSION_STAT_MIN_LATENCY] < total_stats.min_latency) ? + stats[SAI_TWAMP_SESSION_STAT_MIN_LATENCY] : total_stats.min_latency); + total_stats.avg_latency_total += stats[SAI_TWAMP_SESSION_STAT_AVG_LATENCY]; + total_stats.avg_latency = total_stats.avg_latency_total / index; + + /* jitter */ + total_stats.max_jitter = (stats[SAI_TWAMP_SESSION_STAT_MAX_JITTER] > total_stats.max_jitter) ? + stats[SAI_TWAMP_SESSION_STAT_MAX_JITTER] : total_stats.max_jitter; + total_stats.min_jitter = (index == 1) ? stats[SAI_TWAMP_SESSION_STAT_MIN_JITTER] : + ((stats[SAI_TWAMP_SESSION_STAT_MIN_JITTER] < total_stats.min_jitter) ? + stats[SAI_TWAMP_SESSION_STAT_MIN_JITTER] : total_stats.min_jitter); + total_stats.avg_jitter_total += stats[SAI_TWAMP_SESSION_STAT_AVG_JITTER]; + total_stats.avg_jitter = total_stats.avg_jitter_total / index; +} + +void TwampOrch::saveCountersTotal(const string& name, const sai_object_id_t session_id) +{ + SWSS_LOG_ENTER(); + + vector values; + + auto it = m_twampStatistics.find(name); + if (it == m_twampStatistics.end()) + { + SWSS_LOG_ERROR("Failed to caculate non-existent twamp session %s", + name.c_str()); + return; + } + + TwampStats& total_stats = it->second; + + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_RX_PACKETS), to_string(total_stats.rx_packets)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_RX_BYTE), to_string(total_stats.rx_bytes)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_TX_PACKETS), to_string(total_stats.tx_packets)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_TX_BYTE), to_string(total_stats.tx_bytes)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_DROP_PACKETS), to_string(total_stats.drop_packets)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_MAX_LATENCY), to_string(total_stats.max_latency)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_MIN_LATENCY), to_string(total_stats.min_latency)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_AVG_LATENCY), to_string(total_stats.avg_latency)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_MAX_JITTER), to_string(total_stats.max_jitter)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_MIN_JITTER), to_string(total_stats.min_jitter)); + values.emplace_back(sai_serialize_twamp_session_stat(SAI_TWAMP_SESSION_STAT_AVG_JITTER), to_string(total_stats.avg_jitter)); + + m_countersTable->set(sai_serialize_object_id(session_id), values); +} + +void TwampOrch::doTask(NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + if (!m_portsOrch->allPortsReady()) + { + return; + } + + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + if (&consumer != m_twampNotificationConsumer) + { + return; + } + + if (op == "twamp_session_event") + { + uint32_t count = 0; + sai_twamp_session_event_notification_data_t *twamp_session = nullptr; + + sai_deserialize_twamp_session_event_ntf(data, count, &twamp_session); + + for (uint32_t i = 0; i < count; i++) + { + string name; + sai_object_id_t session_id = twamp_session[i].twamp_session_id; + sai_twamp_session_state_t session_state = twamp_session[i].session_state; + uint32_t stats_index = twamp_session[i].session_stats.index; + + if (!getSessionName(session_id, name)) + { + continue; + } + + /* update state db */ + if (session_state == SAI_TWAMP_SESSION_STATE_ACTIVE) + { + setSessionStatus(name, TWAMP_SESSION_STATUS_ACTIVE); + } + else + { + setSessionStatus(name, TWAMP_SESSION_STATUS_INACTIVE); + } + + /* save counter db */ + if (twamp_session[i].session_stats.number_of_counters) + { + if (0 == stats_index) + { + continue; + } + else if (1 == stats_index) + { + addCounterNameMap(name, session_id); + } + + vector hw_stats; + hw_stats.resize(twamp_session_stat_ids.size()); + for (uint32_t j = 0; j < twamp_session[i].session_stats.number_of_counters; j++) + { + uint32_t counters_id = twamp_session[i].session_stats.counters_ids[j]; + auto it = find(twamp_session_stat_ids.begin(), twamp_session_stat_ids.end(), counters_id); + if (it != twamp_session_stat_ids.end()) + { + hw_stats[counters_id] = twamp_session[i].session_stats.counters[j]; + } + } + + saveSessionStatsLatest(session_id, stats_index, hw_stats); + calculateCounters(name, stats_index, hw_stats); + saveCountersTotal(name, session_id); + } + } + + sai_deserialize_free_twamp_session_event_ntf(count, twamp_session); + } +} diff --git a/orchagent/twamporch.h b/orchagent/twamporch.h new file mode 100644 index 0000000000..09134f6be4 --- /dev/null +++ b/orchagent/twamporch.h @@ -0,0 +1,136 @@ +#ifndef SWSS_TWAMPORCH_H +#define SWSS_TWAMPORCH_H + +#include "orch.h" +#include "observer.h" +#include "switchorch.h" +#include "portsorch.h" +#include "vrforch.h" +#include "ipaddress.h" +#include "table.h" +#include + +struct TwampStats +{ + uint64_t rx_packets; + uint64_t rx_bytes; + uint64_t tx_packets; + uint64_t tx_bytes; + uint64_t drop_packets; + uint64_t max_latency; + uint64_t min_latency; + uint64_t avg_latency; + uint64_t max_jitter; + uint64_t min_jitter; + uint64_t avg_jitter; + uint64_t avg_latency_total; + uint64_t avg_jitter_total; +}; + +struct TwampEntry +{ + uint8_t mode; /* twamp mode: full, light */ + uint8_t role; /* sender, reflector */ + bool admin_state; /* test packet state. enabled, disabled */ + bool hw_lookup; + + sai_object_id_t vrf_id; + IpAddress src_ip; + IpAddress dst_ip; + uint16_t src_udp_port; + uint16_t dst_udp_port; + uint16_t padding_size; + uint8_t dscp; + uint8_t ttl; + uint8_t timestamp_format; + + /* sender attr */ + string tx_mode; + uint32_t packet_count; + uint32_t monitor_time; /* second */ + uint32_t tx_interval; /* millisecond */ + uint32_t statistics_interval; /* millisecond */ + uint8_t timeout; /* second */ + + sai_object_id_t session_id; + + TwampEntry() + { + session_id = 0; + admin_state = false; + hw_lookup = true; + vrf_id = 0; + packet_count = 0; + monitor_time = 0; + tx_interval = 0; + statistics_interval = 0; + timeout = 0; + }; +}; + +typedef map TwampEntryTable; +typedef map TwampStatsTable; + +class TwampOrch : public Orch +{ +public: + TwampOrch(TableConnector confDbConnector, TableConnector stateDbConnector, + SwitchOrch *switchOrch, PortsOrch *portOrch, VRFOrch *vrfOrch); + + ~TwampOrch() + { + // do nothing + } + + bool isSessionExists(const string&); + bool getSessionName(const sai_object_id_t oid, string& name); + +private: + SwitchOrch *m_switchOrch; + PortsOrch *m_portsOrch; + VRFOrch *m_vrfOrch; + NotificationConsumer* m_twampNotificationConsumer; + bool register_event_notif; + + unsigned int m_twampSessionCount; + unsigned int m_maxTwampSessionCount; + + TwampEntryTable m_twampEntries; + TwampStatsTable m_twampStatistics; + + shared_ptr m_asicDb; + shared_ptr m_countersDb; + unique_ptr
m_counterTwampSessionNameMapTable; + unique_ptr
m_countersTable; + unique_ptr
m_vidToRidTable; + Table m_stateDbTwampTable; + + bool validateUdpPort(uint16_t udp_port); + void increaseTwampSessionCount(void); + void decreaseTwampSessionCount(void); + bool checkTwampSessionCount(void); + + void setSessionStatus(const string&, const string&); + bool getSessionStatus(const string&, string&); + void removeSessionStatus(const string&); + void removeSessionCounter(const sai_object_id_t); + void initSessionStats(const string&); + + bool registerTwampEventNotification(void); + bool activateSession(const string&, TwampEntry&); + bool deactivateSession(const string&, TwampEntry&); + bool setSessionTransmitEn(TwampEntry&, string test_start); + + task_process_status createEntry(const string&, const vector&); + task_process_status updateEntry(const string&, const vector&); + task_process_status deleteEntry(const string&); + void doTask(Consumer& consumer); + + bool addCounterNameMap(const string&, const sai_object_id_t session_id); + void saveSessionStatsLatest(const sai_object_id_t session_id, const uint32_t index, const vector& stats); + void calculateCounters(const string&, const uint32_t index, const vector& stats); + void saveCountersTotal(const string&, const sai_object_id_t session_id); + void doTask(NotificationConsumer& consumer); +}; + +#endif /* SWSS_TWAMPORCH_H */ diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 776cf1eb0f..93785dfe1e 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -13,12 +13,14 @@ #include "vxlanorch.h" #include "flowcounterrouteorch.h" #include "directory.h" +#include "routeorch.h" using namespace std; using namespace swss; extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_object_id_t gSwitchId; +extern MacAddress gMacAddress; extern Directory gDirectory; extern PortsOrch* gPortsOrch; @@ -104,6 +106,22 @@ bool VRFOrch::addOperation(const Request& request) } } + RouteOrch* routeOrch = gDirectory.get(); + + /* Add link-local fe80::/10 CPU route for the VRF. */ + IpPrefix default_link_local_prefix("fe80::/10"); + routeOrch->addLinkLocalRouteToMe(router_id, default_link_local_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu in VRF %s", default_link_local_prefix.to_string().c_str(), vrf_name.c_str()); + + /* All the interfaces have the same MAC address and hence the same + * auto-generated link-local ipv6 address with eui64 interface-id. + * Hence add a single /128 route entry for the link-local interface + * address pointing to the CPU port. + */ + IpPrefix linklocal_prefix = routeOrch->getLinkLocalEui64Addr(gMacAddress); + routeOrch->addLinkLocalRouteToMe(router_id, linklocal_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu in VRF %s", linklocal_prefix.to_string().c_str(), vrf_name.c_str()); + vrf_table_[vrf_name].vrf_id = router_id; vrf_table_[vrf_name].ref_count = 0; vrf_id_table_[router_id] = vrf_name; @@ -169,6 +187,19 @@ bool VRFOrch::delOperation(const Request& request) return false; sai_object_id_t router_id = vrf_table_[vrf_name].vrf_id; + + RouteOrch* routeOrch = gDirectory.get(); + + /* Delete link-local ipv6 address with eui64 /128 CPU route for the VRF. */ + IpPrefix linklocal_prefix = routeOrch->getLinkLocalEui64Addr(gMacAddress); + routeOrch->delLinkLocalRouteToMe(router_id, linklocal_prefix); + SWSS_LOG_NOTICE("Deleted link local ipv6 route %s to cpu in VRF %s", linklocal_prefix.to_string().c_str(), vrf_name.c_str()); + + /* Delete link-local fe80::/10 CPU route for the VRF. */ + IpPrefix default_link_local_prefix("fe80::/10"); + routeOrch->delLinkLocalRouteToMe(router_id, default_link_local_prefix); + SWSS_LOG_NOTICE("Deleted link local ipv6 route %s to cpu in VRF %s", default_link_local_prefix.to_string().c_str(), vrf_name.c_str()); + sai_status_t status = sai_virtual_router_api->remove_virtual_router(router_id); if (status != SAI_STATUS_SUCCESS) { diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h index 07e0df55ec..2238e1f210 100644 --- a/orchagent/vrforch.h +++ b/orchagent/vrforch.h @@ -88,6 +88,17 @@ class VRFOrch : public Orch2 } } + std::vector getVRFids(void) const + { + std::vector vrf_ids; + for (auto kv: vrf_id_table_) + { + vrf_ids.push_back(kv.first); + } + + return vrf_ids; + } + void increaseVrfRefCount(const std::string& name) { if (vrf_table_.find(name) != std::end(vrf_table_)) diff --git a/tests/conftest.py b/tests/conftest.py index ef95cd96bd..93f54c824e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -29,6 +29,7 @@ from dvslib import dvs_policer from dvslib import dvs_hash from dvslib import dvs_switch +from dvslib import dvs_twamp from buffer_model import enable_dynamic_buffer @@ -1992,6 +1993,14 @@ def dvs_hash_manager(request, dvs): def dvs_switch_manager(request, dvs): request.cls.dvs_switch = dvs_switch.DVSSwitch(dvs.get_asic_db()) +@pytest.fixture(scope="class") +def dvs_twamp_manager(request, dvs): + request.cls.dvs_twamp = dvs_twamp.DVSTwamp(dvs.get_asic_db(), + dvs.get_config_db(), + dvs.get_state_db(), + dvs.get_counters_db(), + dvs.get_app_db()) + ##################### DPB fixtures ########################################### def create_dpb_config_file(dvs): cmd = "sonic-cfggen -j /etc/sonic/init_cfg.json -j /tmp/ports.json --print-data > /tmp/dpb_config_db.json" diff --git a/tests/dvslib/dvs_acl.py b/tests/dvslib/dvs_acl.py index 4315da3798..236ccaa0fc 100644 --- a/tests/dvslib/dvs_acl.py +++ b/tests/dvslib/dvs_acl.py @@ -685,6 +685,17 @@ def _match_acl_range(sai_acl_range): return True return _match_acl_range + + def get_acl_counter_oid(self, acl_rule_id=None) -> str: + if not acl_rule_id: + acl_rule_id = self._get_acl_rule_id() + + entry = self.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", acl_rule_id) + counter_oid = entry.get("SAI_ACL_ENTRY_ATTR_ACTION_COUNTER") + return counter_oid + + def get_acl_rule_id(self) -> str: + return self._get_acl_rule_id() def _get_acl_rule_id(self) -> str: num_keys = len(self.asic_db.default_acl_entries) + 1 @@ -742,7 +753,12 @@ def _check_acl_entry_counters_map(self, acl_entry_oid: str): return rule_to_counter_map = self.counters_db.get_entry("ACL_COUNTER_RULE_MAP", "") counter_to_rule_map = {v: k for k, v in rule_to_counter_map.items()} - assert counter_oid in counter_to_rule_map + assert counter_oid in counter_to_rule_map + + def check_acl_counter_not_in_counters_map(self, acl_counter_oid: str): + rule_to_counter_map = self.counters_db.get_entry("ACL_COUNTER_RULE_MAP", "") + counter_to_rule_map = {v: k for k, v in rule_to_counter_map.items()} + assert acl_counter_oid not in counter_to_rule_map def verify_acl_table_status( self, diff --git a/tests/dvslib/dvs_twamp.py b/tests/dvslib/dvs_twamp.py new file mode 100644 index 0000000000..864b072bd6 --- /dev/null +++ b/tests/dvslib/dvs_twamp.py @@ -0,0 +1,98 @@ +"""Utilities for interacting with TWAMP Light objects when writing VS tests.""" + +class DVSTwamp(object): + def __init__(self, adb, cdb, sdb, cntrdb, appdb): + self.asic_db = adb + self.config_db = cdb + self.state_db = sdb + self.counters_db = cntrdb + self.app_db = appdb + + def create_twamp_light_session_sender_packet_count(self, name, sip, sport, dip, dport, packet_count=100, tx_interval=100, timeout=5, stats_interval=None): + twamp_light_entry = {"mode": "LIGHT", + "role": "SENDER", + "src_ip": sip, + "src_udp_port": sport, + "dst_ip": dip, + "dst_udp_port": dport, + "packet_count": packet_count, + "tx_interval": tx_interval, + "timeout": timeout + } + if stats_interval: + twamp_light_entry["statistics_interval"] = str(stats_interval) + else: + twamp_light_entry["statistics_interval"] = str(int(packet_count) * int(tx_interval) + int(timeout)*1000) + self.config_db.create_entry("TWAMP_SESSION", name, twamp_light_entry) + + def create_twamp_light_session_sender_continuous(self, name, sip, sport, dip, dport, monitor_time=0, tx_interval=100, timeout=5, stats_interval=None): + twamp_light_entry = {"mode": "LIGHT", + "role": "SENDER", + "src_ip": sip, + "src_udp_port": sport, + "dst_ip": dip, + "dst_udp_port": dport, + "monitor_time": monitor_time, + "tx_interval": tx_interval, + "timeout": timeout + } + if stats_interval: + twamp_light_entry["statistics_interval"] = str(stats_interval) + else: + twamp_light_entry["statistics_interval"] = str(int(monitor_time)*1000) + self.config_db.create_entry("TWAMP_SESSION", name, twamp_light_entry) + + def create_twamp_light_session_reflector(self, name, sip, sport, dip, dport): + twamp_light_entry = {"mode": "LIGHT", + "role": "REFLECTOR", + "src_ip": sip, + "src_udp_port": sport, + "dst_ip": dip, + "dst_udp_port": dport + } + self.config_db.create_entry("TWAMP_SESSION", name, twamp_light_entry) + + def start_twamp_light_sender(self, name): + twamp_light_entry = {"admin_state": "enabled"} + self.config_db.create_entry("TWAMP_SESSION", name, twamp_light_entry) + + def stop_twamp_light_sender(self, name): + twamp_light_entry = {"admin_state": "disabled"} + self.config_db.create_entry("TWAMP_SESSION", name, twamp_light_entry) + + def remove_twamp_light_session(self, name): + self.config_db.delete_entry("TWAMP_SESSION", name) + + def get_twamp_light_session_status(self, name): + return self.get_twamp_light_session_state(name)["status"] + + def get_twamp_light_session_state(self, name): + tbl = swsscommon.Table(self.sdb, "TWAMP_SESSION_TABLE") + (status, fvs) = tbl.get(name) + assert status == True + assert len(fvs) > 0 + return { fv[0]: fv[1] for fv in fvs } + + def verify_session_status(self, name, status="active", expected=1): + self.state_db.wait_for_n_keys("TWAMP_SESSION_TABLE", expected) + if expected: + self.state_db.wait_for_field_match("TWAMP_SESSION_TABLE", name, {"status": status}) + + def verify_no_session(self): + self.config_db.wait_for_n_keys("TWAMP_SESSION", 0) + self.state_db.wait_for_n_keys("TWAMP_SESSION_TABLE", 0) + + def verify_session_asic_db(self, dvs, name, asic_table=None, expected=1): + session_oids = self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_TWAMP_SESSION", expected) + session_oid = session_oids[0] + dvs.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_TWAMP_SESSION", session_oid, asic_table) + + def verify_session_counter_db(self, dvs, name, counter_table=None, expected=1, expected_item=1): + fvs = dvs.counters_db.get_entry("COUNTERS_TWAMP_SESSION_NAME_MAP", "") + fvs = dict(fvs) + total_key = self.counters_db.db_connection.keys("COUNTERS:{}".format(fvs[name])) + assert len(total_key) == expected, "TWAMP Light counter entries are not available in counter db" + dvs.counters_db.wait_for_field_match("COUNTERS", fvs[name], counter_table) + item_keys = self.counters_db.db_connection.keys("COUNTERS:{}:INDEX:*".format(fvs[name])) + assert len(item_keys) == expected_item, "TWAMP Light counter entries are not available in counter db" + diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index bbe8b5db37..96c95b121b 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -40,10 +40,12 @@ tests_SOURCES = aclorch_ut.cpp \ mock_orchagent_main.cpp \ mock_dbconnector.cpp \ mock_consumerstatetable.cpp \ + mock_subscriberstatetable.cpp \ common/mock_shell_command.cpp \ mock_table.cpp \ mock_hiredis.cpp \ mock_redisreply.cpp \ + mock_sai_api.cpp \ bulker_ut.cpp \ portmgr_ut.cpp \ sflowmgrd_ut.cpp \ @@ -56,6 +58,8 @@ tests_SOURCES = aclorch_ut.cpp \ warmrestartassist_ut.cpp \ test_failure_handling.cpp \ warmrestarthelper_ut.cpp \ + neighorch_ut.cpp \ + twamporch_ut.cpp \ $(top_srcdir)/warmrestart/warmRestartHelper.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ $(top_srcdir)/lib/subintf.cpp \ @@ -128,7 +132,9 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/dash/dashvnetorch.cpp \ $(top_srcdir)/cfgmgr/buffermgrdyn.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ - $(top_srcdir)/orchagent/dash/pbutils.cpp + $(top_srcdir)/orchagent/dash/pbutils.cpp \ + $(top_srcdir)/cfgmgr/coppmgr.cpp \ + $(top_srcdir)/orchagent/twamporch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 8005199935..b8b089e3df 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -384,8 +384,13 @@ namespace aclorch_test ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; diff --git a/tests/mock_tests/bufferorch_ut.cpp b/tests/mock_tests/bufferorch_ut.cpp index 2cd15ee549..fdcd5396f7 100644 --- a/tests/mock_tests/bufferorch_ut.cpp +++ b/tests/mock_tests/bufferorch_ut.cpp @@ -265,8 +265,13 @@ namespace bufferorch_test ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; diff --git a/tests/mock_tests/copp_ut.cpp b/tests/mock_tests/copp_ut.cpp index 1c3b766e1c..f5d0b85cf5 100644 --- a/tests/mock_tests/copp_ut.cpp +++ b/tests/mock_tests/copp_ut.cpp @@ -4,34 +4,14 @@ #include "warm_restart.h" #include "ut_helper.h" #include "coppmgr.h" -#include "coppmgr.cpp" #include #include + using namespace std; using namespace swss; -void create_init_file() -{ - int status = system("sudo mkdir /etc/sonic/"); - ASSERT_EQ(status, 0); - - status = system("sudo chmod 777 /etc/sonic/"); - ASSERT_EQ(status, 0); - - status = system("sudo cp copp_cfg.json /etc/sonic/"); - ASSERT_EQ(status, 0); -} - -void cleanup() -{ - int status = system("sudo rm -rf /etc/sonic/"); - ASSERT_EQ(status, 0); -} - TEST(CoppMgrTest, CoppTest) { - create_init_file(); - const vector cfg_copp_tables = { CFG_COPP_TRAP_TABLE_NAME, CFG_COPP_GROUP_TABLE_NAME, @@ -65,12 +45,10 @@ TEST(CoppMgrTest, CoppTest) {"trap_ids", "ip2me"} }); - CoppMgr coppmgr(&cfgDb, &appDb, &stateDb, cfg_copp_tables); + CoppMgr coppmgr(&cfgDb, &appDb, &stateDb, cfg_copp_tables, "./copp_cfg.json"); string overide_val; coppTable.hget("queue1_group1", "cbs",overide_val); EXPECT_EQ( overide_val, "6000"); - - cleanup(); } diff --git a/tests/mock_tests/fake_producerstatetable.cpp b/tests/mock_tests/fake_producerstatetable.cpp index 6221556f63..33fab17ecf 100644 --- a/tests/mock_tests/fake_producerstatetable.cpp +++ b/tests/mock_tests/fake_producerstatetable.cpp @@ -4,8 +4,13 @@ using namespace std; namespace swss { + ProducerStateTable::ProducerStateTable(RedisPipeline *pipeline, const string &tableName, bool buffered) - : TableBase(tableName, SonicDBConfig::getSeparator(pipeline->getDBConnector())), TableName_KeySet(tableName) {} + : TableBase(tableName, SonicDBConfig::getSeparator(pipeline->getDBConnector())), TableName_KeySet(tableName), m_buffered(buffered) + , m_pipeowned(false) + , m_tempViewActive(false) + , m_pipe(pipeline) {} ProducerStateTable::~ProducerStateTable() {} + } diff --git a/tests/mock_tests/flowcounterrouteorch_ut.cpp b/tests/mock_tests/flowcounterrouteorch_ut.cpp index cf80bda5bf..382dd9cf9b 100644 --- a/tests/mock_tests/flowcounterrouteorch_ut.cpp +++ b/tests/mock_tests/flowcounterrouteorch_ut.cpp @@ -135,6 +135,7 @@ namespace flowcounterrouteorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, @@ -156,8 +157,13 @@ namespace flowcounterrouteorch_test gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); gDirectory.set(gVrfOrch); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; @@ -397,4 +403,4 @@ namespace flowcounterrouteorch_test vrf_consumer->addToSync(entries); static_cast(gVrfOrch)->doTask(); } -} \ No newline at end of file +} diff --git a/tests/mock_tests/fpmsyncd/test_routesync.cpp b/tests/mock_tests/fpmsyncd/test_routesync.cpp index debfa16d21..a8de78859f 100644 --- a/tests/mock_tests/fpmsyncd/test_routesync.cpp +++ b/tests/mock_tests/fpmsyncd/test_routesync.cpp @@ -1,12 +1,32 @@ -#include "fpmsyncd/routesync.h" +#include "redisutility.h" #include #include +#include "mock_table.h" +#define private public +#include "fpmsyncd/routesync.h" +#undef private using namespace swss; +#define MAX_PAYLOAD 1024 using ::testing::_; +class MockRouteSync : public RouteSync +{ +public: + MockRouteSync(RedisPipeline *m_pipeline) : RouteSync(m_pipeline) + { + } + + ~MockRouteSync() + { + } + MOCK_METHOD(bool, getEvpnNextHop, (nlmsghdr *, int, + rtattr *[], std::string&, + std::string& , std::string&, + std::string&), (override)); +}; class MockFpm : public FpmInterface { public: @@ -42,10 +62,11 @@ class FpmSyncdResponseTest : public ::testing::Test { } - DBConnector m_db{"APPL_DB", 0}; - RedisPipeline m_pipeline{&m_db, 1}; - RouteSync m_routeSync{&m_pipeline}; + shared_ptr m_db = make_shared("APPL_DB", 0); + shared_ptr m_pipeline = make_shared(m_db.get()); + RouteSync m_routeSync{m_pipeline.get()}; MockFpm m_mockFpm{&m_routeSync}; + MockRouteSync m_mockRouteSync{m_pipeline.get()}; }; TEST_F(FpmSyncdResponseTest, RouteResponseFeedbackV4) @@ -170,3 +191,44 @@ TEST_F(FpmSyncdResponseTest, WarmRestart) m_routeSync.onWarmStartEnd(applStateDb); } + +TEST_F(FpmSyncdResponseTest, testEvpn) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *) malloc(NLMSG_SPACE(MAX_PAYLOAD)); + shared_ptr m_app_db; + m_app_db = make_shared("APPL_DB", 0); + Table app_route_table(m_app_db.get(), APP_ROUTE_TABLE_NAME); + + memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); + nlh->nlmsg_type = RTM_NEWROUTE; + struct rtmsg rtm; + rtm.rtm_family = AF_INET; + rtm.rtm_protocol = 200; + rtm.rtm_type = RTN_UNICAST; + rtm.rtm_table = 0; + rtm.rtm_dst_len = 32; + nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); + memcpy(NLMSG_DATA(nlh), &rtm, sizeof(rtm)); + + EXPECT_CALL(m_mockRouteSync, getEvpnNextHop(_, _, _, _, _, _, _)).Times(testing::AtLeast(1)).WillOnce([&]( + struct nlmsghdr *h, int received_bytes, + struct rtattr *tb[], std::string& nexthops, + std::string& vni_list, std::string& mac_list, + std::string& intf_list)-> bool { + vni_list="100"; + mac_list="aa:aa:aa:aa:aa:aa"; + intf_list="Ethernet0"; + nexthops = "1.1.1.1"; + return true; + }); + m_mockRouteSync.onMsgRaw(nlh); + vector keys; + vector fieldValues; + app_route_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1); + + app_route_table.get(keys[0], fieldValues); + auto value = swss::fvsGetValue(fieldValues, "protocol", true); + ASSERT_EQ(value.get(), "0xc8"); + +} diff --git a/tests/mock_tests/intfsorch_ut.cpp b/tests/mock_tests/intfsorch_ut.cpp index ffbf348ed4..dead6cf0a1 100644 --- a/tests/mock_tests/intfsorch_ut.cpp +++ b/tests/mock_tests/intfsorch_ut.cpp @@ -148,8 +148,13 @@ namespace intfsorch_test gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); gDirectory.set(gVrfOrch); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; @@ -327,4 +332,4 @@ namespace intfsorch_test ASSERT_EQ(current_create_count + 1, create_rif_count); ASSERT_EQ(current_remove_count + 1, remove_rif_count); } -} \ No newline at end of file +} diff --git a/tests/mock_tests/mock_orch_test.h b/tests/mock_tests/mock_orch_test.h new file mode 100644 index 0000000000..9a4b71f658 --- /dev/null +++ b/tests/mock_tests/mock_orch_test.h @@ -0,0 +1,310 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "gtest/gtest.h" +#include + +using namespace std; +using ::testing::Return; +using ::testing::Throw; + +namespace mock_orch_test +{ + static const string PEER_SWITCH_HOSTNAME = "peer_hostname"; + static const string PEER_IPV4_ADDRESS = "1.1.1.1"; + static const string ACTIVE_INTERFACE = "Ethernet4"; + static const string STANDBY_INTERFACE = "Ethernet8"; + static const string ACTIVE_STATE = "active"; + static const string STANDBY_STATE = "standby"; + static const string STATE = "state"; + static const string VLAN_1000 = "Vlan1000"; + static const string VLAN_2000 = "Vlan2000"; + static const string SERVER_IP1 = "192.168.0.2"; + static const string SERVER_IP2 = "192.168.0.3"; + static const string MAC1 = "62:f9:65:10:2f:01"; + static const string MAC2 = "62:f9:65:10:2f:02"; + static const string MAC3 = "62:f9:65:10:2f:03"; + + class MockOrchTest: public ::testing::Test + { + protected: + std::vector ut_orch_list; + shared_ptr m_app_db; + shared_ptr m_config_db; + shared_ptr m_state_db; + shared_ptr m_chassis_app_db; + MuxOrch *m_MuxOrch; + MuxCableOrch *m_MuxCableOrch; + MuxCable *m_MuxCable; + TunnelDecapOrch *m_TunnelDecapOrch; + MuxStateOrch *m_MuxStateOrch; + FlexCounterOrch *m_FlexCounterOrch; + VxlanTunnelOrch *m_VxlanTunnelOrch; + + virtual void ApplyInitialConfigs() {} + + void PrepareSai() + { + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + sai_status_t status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + + /* Create a loopback underlay router interface */ + vector underlay_intf_attrs; + + sai_attribute_t underlay_intf_attr; + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; + underlay_intf_attr.value.oid = gVirtualRouterId; + underlay_intf_attrs.push_back(underlay_intf_attr); + + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + underlay_intf_attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_LOOPBACK; + underlay_intf_attrs.push_back(underlay_intf_attr); + + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; + underlay_intf_attr.value.u32 = 9100; + underlay_intf_attrs.push_back(underlay_intf_attr); + + status = sai_router_intfs_api->create_router_interface(&gUnderlayIfId, gSwitchId, (uint32_t)underlay_intf_attrs.size(), underlay_intf_attrs.data()); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + virtual void PostSetUp() {}; + + void SetUp() override + { + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); + + PrepareSai(); + + const int portsorch_base_pri = 40; + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + + m_FlexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(m_FlexCounterOrch); + ut_orch_list.push_back((Orch **)&m_FlexCounterOrch); + + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); + gDirectory.set(gFlowCounterRouteOrch); + ut_orch_list.push_back((Orch **)&gFlowCounterRouteOrch); + + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + gDirectory.set(gVrfOrch); + ut_orch_list.push_back((Orch **)&gVrfOrch); + + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); + gDirectory.set(gIntfsOrch); + ut_orch_list.push_back((Orch **)&gIntfsOrch); + + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); + ut_orch_list.push_back((Orch **)&gPortsOrch); + + const int fgnhgorch_pri = 15; + + vector fgnhg_tables = { + { CFG_FG_NHG, fgnhgorch_pri }, + { CFG_FG_NHG_PREFIX, fgnhgorch_pri }, + { CFG_FG_NHG_MEMBER, fgnhgorch_pri } + }; + + gFgNhgOrch = new FgNhgOrch(m_config_db.get(), m_app_db.get(), m_state_db.get(), fgnhg_tables, gNeighOrch, gIntfsOrch, gVrfOrch); + gDirectory.set(gFgNhgOrch); + ut_orch_list.push_back((Orch **)&gFgNhgOrch); + + const int fdborch_pri = 20; + + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri } + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); + gDirectory.set(gFdbOrch); + ut_orch_list.push_back((Orch **)&gFdbOrch); + + gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); + gDirectory.set(gNeighOrch); + ut_orch_list.push_back((Orch **)&gNeighOrch); + + m_TunnelDecapOrch = new TunnelDecapOrch(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); + gDirectory.set(m_TunnelDecapOrch); + ut_orch_list.push_back((Orch **)&m_TunnelDecapOrch); + vector mux_tables = { + CFG_MUX_CABLE_TABLE_NAME, + CFG_PEER_SWITCH_TABLE_NAME + }; + + vector buffer_tables = { + APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME + }; + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); + + TableConnector stateDbSwitchTable(m_state_db.get(), STATE_SWITCH_CAPABILITY_TABLE_NAME); + TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); + TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); + + vector switch_tables = { + conf_asic_sensors, + app_switch_table + }; + vector policer_tables = { + TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), + TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + + TableConnector stateDbStorm(m_state_db.get(), STATE_BUM_STORM_CAPABILITY_TABLE_NAME); + gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); + gDirectory.set(gPolicerOrch); + ut_orch_list.push_back((Orch **)&gPolicerOrch); + + gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); + gDirectory.set(gSwitchOrch); + ut_orch_list.push_back((Orch **)&gSwitchOrch); + + gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); + gDirectory.set(gNhgOrch); + ut_orch_list.push_back((Orch **)&gNhgOrch); + + vector srv6_tables = { + APP_SRV6_SID_LIST_TABLE_NAME, + APP_SRV6_MY_SID_TABLE_NAME + }; + gSrv6Orch = new Srv6Orch(m_app_db.get(), srv6_tables, gSwitchOrch, gVrfOrch, gNeighOrch); + gDirectory.set(gSrv6Orch); + ut_orch_list.push_back((Orch **)&gSrv6Orch); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + gDirectory.set(gCrmOrch); + ut_orch_list.push_back((Orch **)&gCrmOrch); + + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gDirectory.set(gRouteOrch); + ut_orch_list.push_back((Orch **)&gRouteOrch); + TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); + TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); + gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); + gDirectory.set(gMirrorOrch); + ut_orch_list.push_back((Orch **)&gMirrorOrch); + + TableConnector confDbAclTable(m_config_db.get(), CFG_ACL_TABLE_TABLE_NAME); + TableConnector confDbAclTableType(m_config_db.get(), CFG_ACL_TABLE_TYPE_TABLE_NAME); + TableConnector confDbAclRuleTable(m_config_db.get(), CFG_ACL_RULE_TABLE_NAME); + TableConnector appDbAclTable(m_app_db.get(), APP_ACL_TABLE_TABLE_NAME); + TableConnector appDbAclTableType(m_app_db.get(), APP_ACL_TABLE_TYPE_TABLE_NAME); + TableConnector appDbAclRuleTable(m_app_db.get(), APP_ACL_RULE_TABLE_NAME); + + vector acl_table_connectors = { + confDbAclTableType, + confDbAclTable, + confDbAclRuleTable, + appDbAclTable, + appDbAclRuleTable, + appDbAclTableType, + }; + gAclOrch = new AclOrch(acl_table_connectors, m_state_db.get(), + gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, NULL); + gDirectory.set(gAclOrch); + ut_orch_list.push_back((Orch **)&gAclOrch); + + m_MuxOrch = new MuxOrch(m_config_db.get(), mux_tables, m_TunnelDecapOrch, gNeighOrch, gFdbOrch); + gDirectory.set(m_MuxOrch); + ut_orch_list.push_back((Orch **)&m_MuxOrch); + + m_MuxCableOrch = new MuxCableOrch(m_app_db.get(), m_state_db.get(), APP_MUX_CABLE_TABLE_NAME); + gDirectory.set(m_MuxCableOrch); + ut_orch_list.push_back((Orch **)&m_MuxCableOrch); + + m_MuxStateOrch = new MuxStateOrch(m_state_db.get(), STATE_HW_MUX_CABLE_TABLE_NAME); + gDirectory.set(m_MuxStateOrch); + ut_orch_list.push_back((Orch **)&m_MuxStateOrch); + + m_VxlanTunnelOrch = new VxlanTunnelOrch(m_state_db.get(), m_app_db.get(), APP_VXLAN_TUNNEL_TABLE_NAME); + gDirectory.set(m_VxlanTunnelOrch); + ut_orch_list.push_back((Orch **)&m_VxlanTunnelOrch); + + ApplyInitialConfigs(); + PostSetUp(); + } + + virtual void PreTearDown() {}; + + void TearDown() override + { + PreTearDown(); + for (std::vector::reverse_iterator rit = ut_orch_list.rbegin(); rit != ut_orch_list.rend(); ++rit) + { + Orch **orch = *rit; + delete *orch; + *orch = nullptr; + } + + gDirectory.m_values.clear(); + + ut_helper::uninitSaiApi(); + } + }; +} \ No newline at end of file diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 93c1588b9b..850bcb7ed2 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -27,6 +27,7 @@ #include "muxorch.h" #include "nhgorch.h" #include "copporch.h" +#include "twamporch.h" #include "directory.h" extern int gBatchSize; @@ -86,3 +87,4 @@ extern sai_mpls_api_t* sai_mpls_api; extern sai_counter_api_t* sai_counter_api; extern sai_samplepacket_api_t *sai_samplepacket_api; extern sai_fdb_api_t* sai_fdb_api; +extern sai_twamp_api_t* sai_twamp_api; diff --git a/tests/mock_tests/mock_sai_api.cpp b/tests/mock_tests/mock_sai_api.cpp new file mode 100644 index 0000000000..1f7e7e63ef --- /dev/null +++ b/tests/mock_tests/mock_sai_api.cpp @@ -0,0 +1,25 @@ +#include "mock_sai_api.h" + +std::set apply_mock_fns; +std::set remove_mock_fns; + +void MockSaiApis() +{ + if (apply_mock_fns.empty()) + { + EXPECT_TRUE(false) << "No mock application functions found. Did you call DEFINE_SAI_API_MOCK and INIT_SAI_API_MOCK for the necessary SAI object type?"; + } + + for (auto apply_fn : apply_mock_fns) + { + (*apply_fn)(); + } +} + +void RestoreSaiApis() +{ + for (auto remove_fn : remove_mock_fns) + { + (*remove_fn)(); + } +} \ No newline at end of file diff --git a/tests/mock_tests/mock_sai_api.h b/tests/mock_tests/mock_sai_api.h index 63d8921bf1..7819b5b126 100644 --- a/tests/mock_tests/mock_sai_api.h +++ b/tests/mock_tests/mock_sai_api.h @@ -1,11 +1,26 @@ +#ifndef MOCK_SAI_API_H +#define MOCK_SAI_API_H #include "mock_orchagent_main.h" #include +/* +To mock a particular SAI API: +1. At the top of the test CPP file using the mock, call DEFINE_SAI_API_MOCK or DEFINE_SAI_GENERIC_API_MOCK + for each SAI API you want to mock. +2. At the top of the test CPP file using the mock, call EXTERN_MOCK_FNS. +3. In the SetUp method of the test class, call INIT_SAI_API_MOCK for each SAI API you want to mock. +4. In the SetUp method of the test class, call MockSaiApis. +5. In the TearDown method of the test class, call RestoreSaiApis. +*/ + using ::testing::Return; using ::testing::NiceMock; -std::set apply_mock_fns; -std::set remove_mock_fns; +#define EXTERN_MOCK_FNS \ + extern std::set apply_mock_fns; \ + extern std::set remove_mock_fns; + +EXTERN_MOCK_FNS #define CREATE_PARAMS(sai_object_type) _In_ const sai_##sai_object_type##_entry_t *sai_object_type##_entry, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list #define REMOVE_PARAMS(sai_object_type) _In_ const sai_##sai_object_type##_entry_t *sai_object_type##_entry @@ -27,8 +42,8 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the 7. Define a method to remove the mock */ #define DEFINE_SAI_API_MOCK(sai_object_type) \ - sai_##sai_object_type##_api_t *old_sai_##sai_object_type##_api; \ - sai_##sai_object_type##_api_t ut_sai_##sai_object_type##_api; \ + static sai_##sai_object_type##_api_t *old_sai_##sai_object_type##_api; \ + static sai_##sai_object_type##_api_t ut_sai_##sai_object_type##_api; \ class mock_sai_##sai_object_type##_api_t \ { \ public: \ @@ -48,16 +63,16 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the MOCK_METHOD3(create_##sai_object_type##_entry, sai_status_t(CREATE_PARAMS(sai_object_type))); \ MOCK_METHOD1(remove_##sai_object_type##_entry, sai_status_t(REMOVE_PARAMS(sai_object_type))); \ }; \ - mock_sai_##sai_object_type##_api_t *mock_sai_##sai_object_type##_api; \ - sai_status_t mock_create_##sai_object_type##_entry(CREATE_PARAMS(sai_object_type)) \ + static mock_sai_##sai_object_type##_api_t *mock_sai_##sai_object_type##_api; \ + inline sai_status_t mock_create_##sai_object_type##_entry(CREATE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_object_type##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ } \ - sai_status_t mock_remove_##sai_object_type##_entry(REMOVE_PARAMS(sai_object_type)) \ + inline sai_status_t mock_remove_##sai_object_type##_entry(REMOVE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_object_type##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ } \ - void apply_sai_##sai_object_type##_api_mock() \ + inline void apply_sai_##sai_object_type##_api_mock() \ { \ mock_sai_##sai_object_type##_api = new NiceMock(); \ \ @@ -68,15 +83,15 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the sai_##sai_object_type##_api->create_##sai_object_type##_entry = mock_create_##sai_object_type##_entry; \ sai_##sai_object_type##_api->remove_##sai_object_type##_entry = mock_remove_##sai_object_type##_entry; \ } \ - void remove_sai_##sai_object_type##_api_mock() \ + inline void remove_sai_##sai_object_type##_api_mock() \ { \ sai_##sai_object_type##_api = old_sai_##sai_object_type##_api; \ delete mock_sai_##sai_object_type##_api; \ } #define DEFINE_SAI_GENERIC_API_MOCK(sai_api_name, sai_object_type) \ - sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ - sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ class mock_sai_##sai_api_name##_api_t \ { \ public: \ @@ -96,16 +111,16 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the MOCK_METHOD4(create_##sai_object_type, sai_status_t(GENERIC_CREATE_PARAMS(sai_object_type))); \ MOCK_METHOD1(remove_##sai_object_type, sai_status_t(GENERIC_REMOVE_PARAMS(sai_object_type))); \ }; \ - mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ - sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) \ + static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ + inline sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ } \ - sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) \ + inline sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ } \ - void apply_sai_##sai_api_name##_api_mock() \ + inline void apply_sai_##sai_api_name##_api_mock() \ { \ mock_sai_##sai_api_name##_api = new NiceMock(); \ \ @@ -116,7 +131,7 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the sai_##sai_api_name##_api->create_##sai_object_type = mock_create_##sai_object_type; \ sai_##sai_api_name##_api->remove_##sai_object_type = mock_remove_##sai_object_type; \ } \ - void remove_sai_##sai_api_name##_api_mock() \ + inline void remove_sai_##sai_api_name##_api_mock() \ { \ sai_##sai_api_name##_api = old_sai_##sai_api_name##_api; \ delete mock_sai_##sai_api_name##_api; \ @@ -127,23 +142,6 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the apply_mock_fns.insert(&apply_sai_##sai_object_type##_api_mock); \ remove_mock_fns.insert(&remove_sai_##sai_object_type##_api_mock); -void MockSaiApis() -{ - if (apply_mock_fns.empty()) - { - EXPECT_TRUE(false) << "No mock application functions found. Did you call DEFINE_SAI_API_MOCK and INIT_SAI_API_MOCK for the necessary SAI object type?"; - } - - for (auto apply_fn : apply_mock_fns) - { - (*apply_fn)(); - } -} - -void RestoreSaiApis() -{ - for (auto remove_fn : remove_mock_fns) - { - (*remove_fn)(); - } -} +void MockSaiApis(); +void RestoreSaiApis(); +#endif \ No newline at end of file diff --git a/tests/mock_tests/mock_subscriberstatetable.cpp b/tests/mock_tests/mock_subscriberstatetable.cpp new file mode 100644 index 0000000000..5548191940 --- /dev/null +++ b/tests/mock_tests/mock_subscriberstatetable.cpp @@ -0,0 +1,30 @@ +#include "subscriberstatetable.h" + +namespace swss +{ + SubscriberStateTable::SubscriberStateTable(DBConnector *db, const std::string &tableName, int popBatchSize, int pri) : + ConsumerTableBase(db, tableName, popBatchSize, pri), + m_table(db, tableName) + { + } + + void SubscriberStateTable::pops(std::deque &vkco, const std::string& /*prefix*/) + { + std::vector keys; + m_table.getKeys(keys); + for (const auto &key: keys) + { + KeyOpFieldsValuesTuple kco; + + kfvKey(kco) = key; + kfvOp(kco) = SET_COMMAND; + + if (!m_table.get(key, kfvFieldsValues(kco))) + { + continue; + } + m_table.del(key); + vkco.push_back(kco); + } + } +} diff --git a/tests/mock_tests/mux_rollback_ut.cpp b/tests/mock_tests/mux_rollback_ut.cpp index 578b6c817b..3b6e2557ff 100644 --- a/tests/mock_tests/mux_rollback_ut.cpp +++ b/tests/mock_tests/mux_rollback_ut.cpp @@ -7,45 +7,28 @@ #include "ut_helper.h" #include "mock_orchagent_main.h" #include "mock_sai_api.h" +#include "mock_orch_test.h" #include "gtest/gtest.h" #include -DEFINE_SAI_API_MOCK(neighbor); -DEFINE_SAI_API_MOCK(route); -DEFINE_SAI_GENERIC_API_MOCK(acl, acl_entry); -DEFINE_SAI_GENERIC_API_MOCK(next_hop, next_hop); +EXTERN_MOCK_FNS namespace mux_rollback_test { + DEFINE_SAI_API_MOCK(neighbor); + DEFINE_SAI_API_MOCK(route); + DEFINE_SAI_GENERIC_API_MOCK(acl, acl_entry); + DEFINE_SAI_GENERIC_API_MOCK(next_hop, next_hop); using namespace std; + using namespace mock_orch_test; using ::testing::Return; using ::testing::Throw; - static const string PEER_SWITCH_HOSTNAME = "peer_hostname"; - static const string PEER_IPV4_ADDRESS = "1.1.1.1"; static const string TEST_INTERFACE = "Ethernet4"; - static const string ACTIVE = "active"; - static const string STANDBY = "standby"; - static const string STATE = "state"; - static const string VLAN_NAME = "Vlan1000"; - static const string SERVER_IP = "192.168.0.2"; - class MuxRollbackTest : public ::testing::Test + class MuxRollbackTest : public MockOrchTest { protected: - std::vector ut_orch_list; - shared_ptr m_app_db; - shared_ptr m_config_db; - shared_ptr m_state_db; - shared_ptr m_chassis_app_db; - MuxOrch *m_MuxOrch; - MuxCableOrch *m_MuxCableOrch; - MuxCable *m_MuxCable; - TunnelDecapOrch *m_TunnelDecapOrch; - MuxStateOrch *m_MuxStateOrch; - FlexCounterOrch *m_FlexCounterOrch; - mock_sai_neighbor_api_t mock_sai_neighbor_api_; - void SetMuxStateFromAppDb(std::string state) { Table mux_cable_table = Table(m_app_db.get(), APP_MUX_CABLE_TABLE_NAME); @@ -60,7 +43,7 @@ namespace mux_rollback_test EXPECT_EQ(state, m_MuxCable->getState()); } - void ApplyDualTorConfigs() + void ApplyInitialConfigs() { Table peer_switch_table = Table(m_config_db.get(), CFG_PEER_SWITCH_TABLE_NAME); Table tunnel_table = Table(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); @@ -77,21 +60,21 @@ namespace mux_rollback_test port_table.set("PortInitDone", { {} }); neigh_table.set( - VLAN_NAME + neigh_table.getTableNameSeparator() + SERVER_IP, { { "neigh", "62:f9:65:10:2f:04" }, + VLAN_1000 + neigh_table.getTableNameSeparator() + SERVER_IP1, { { "neigh", "62:f9:65:10:2f:04" }, { "family", "IPv4" } }); - vlan_table.set(VLAN_NAME, { { "admin_status", "up" }, + vlan_table.set(VLAN_1000, { { "admin_status", "up" }, { "mtu", "9100" }, { "mac", "00:aa:bb:cc:dd:ee" } }); vlan_member_table.set( - VLAN_NAME + vlan_member_table.getTableNameSeparator() + TEST_INTERFACE, + VLAN_1000 + vlan_member_table.getTableNameSeparator() + TEST_INTERFACE, { { "tagging_mode", "untagged" } }); - intf_table.set(VLAN_NAME, { { "grat_arp", "enabled" }, + intf_table.set(VLAN_1000, { { "grat_arp", "enabled" }, { "proxy_arp", "enabled" }, { "mac_addr", "00:00:00:00:00:00" } }); intf_table.set( - VLAN_NAME + neigh_table.getTableNameSeparator() + "192.168.0.1/21", { + VLAN_1000 + neigh_table.getTableNameSeparator() + "192.168.0.1/21", { { "scope", "global" }, { "family", "IPv4" }, }); @@ -105,7 +88,7 @@ namespace mux_rollback_test peer_switch_table.set(PEER_SWITCH_HOSTNAME, { { "address_ipv4", PEER_IPV4_ADDRESS } }); - mux_cable_table.set(TEST_INTERFACE, { { "server_ipv4", SERVER_IP + "/32" }, + mux_cable_table.set(TEST_INTERFACE, { { "server_ipv4", SERVER_IP1 + "/32" }, { "server_ipv6", "a::a/128" }, { "state", "auto" } }); @@ -132,237 +115,11 @@ namespace mux_rollback_test m_MuxCable = m_MuxOrch->getMuxCable(TEST_INTERFACE); // We always expect the mux to be initialized to standby - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } - void PrepareSai() + void PostSetUp() override { - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; - attr.value.booldata = true; - - sai_status_t status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // Get switch source MAC address - attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - gMacAddress = attr.value.mac; - - attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - gVirtualRouterId = attr.value.oid; - - /* Create a loopback underlay router interface */ - vector underlay_intf_attrs; - - sai_attribute_t underlay_intf_attr; - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; - underlay_intf_attr.value.oid = gVirtualRouterId; - underlay_intf_attrs.push_back(underlay_intf_attr); - - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; - underlay_intf_attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_LOOPBACK; - underlay_intf_attrs.push_back(underlay_intf_attr); - - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; - underlay_intf_attr.value.u32 = 9100; - underlay_intf_attrs.push_back(underlay_intf_attr); - - status = sai_router_intfs_api->create_router_interface(&gUnderlayIfId, gSwitchId, (uint32_t)underlay_intf_attrs.size(), underlay_intf_attrs.data()); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - } - - void SetUp() override - { - map profile = { - { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, - { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } - }; - - ut_helper::initSaiApi(profile); - m_app_db = make_shared("APPL_DB", 0); - m_config_db = make_shared("CONFIG_DB", 0); - m_state_db = make_shared("STATE_DB", 0); - m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); - - PrepareSai(); - - const int portsorch_base_pri = 40; - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - vector flex_counter_tables = { - CFG_FLEX_COUNTER_TABLE_NAME - }; - - m_FlexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); - gDirectory.set(m_FlexCounterOrch); - ut_orch_list.push_back((Orch **)&m_FlexCounterOrch); - - static const vector route_pattern_tables = { - CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, - }; - gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); - gDirectory.set(gFlowCounterRouteOrch); - ut_orch_list.push_back((Orch **)&gFlowCounterRouteOrch); - - gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); - gDirectory.set(gVrfOrch); - ut_orch_list.push_back((Orch **)&gVrfOrch); - - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); - gDirectory.set(gIntfsOrch); - ut_orch_list.push_back((Orch **)&gIntfsOrch); - - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - gDirectory.set(gPortsOrch); - ut_orch_list.push_back((Orch **)&gPortsOrch); - - const int fgnhgorch_pri = 15; - - vector fgnhg_tables = { - { CFG_FG_NHG, fgnhgorch_pri }, - { CFG_FG_NHG_PREFIX, fgnhgorch_pri }, - { CFG_FG_NHG_MEMBER, fgnhgorch_pri } - }; - - gFgNhgOrch = new FgNhgOrch(m_config_db.get(), m_app_db.get(), m_state_db.get(), fgnhg_tables, gNeighOrch, gIntfsOrch, gVrfOrch); - gDirectory.set(gFgNhgOrch); - ut_orch_list.push_back((Orch **)&gFgNhgOrch); - - const int fdborch_pri = 20; - - vector app_fdb_tables = { - { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, - { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri } - }; - - TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); - TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); - gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); - gDirectory.set(gFdbOrch); - ut_orch_list.push_back((Orch **)&gFdbOrch); - - gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); - gDirectory.set(gNeighOrch); - ut_orch_list.push_back((Orch **)&gNeighOrch); - - m_TunnelDecapOrch = new TunnelDecapOrch(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); - gDirectory.set(m_TunnelDecapOrch); - ut_orch_list.push_back((Orch **)&m_TunnelDecapOrch); - vector mux_tables = { - CFG_MUX_CABLE_TABLE_NAME, - CFG_PEER_SWITCH_TABLE_NAME - }; - - vector buffer_tables = { - APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME - }; - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - - TableConnector stateDbSwitchTable(m_state_db.get(), STATE_SWITCH_CAPABILITY_TABLE_NAME); - TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); - TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); - - vector switch_tables = { - conf_asic_sensors, - app_switch_table - }; - vector policer_tables = { - TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), - TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) - }; - - TableConnector stateDbStorm(m_state_db.get(), STATE_BUM_STORM_CAPABILITY_TABLE_NAME); - gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); - gDirectory.set(gPolicerOrch); - ut_orch_list.push_back((Orch **)&gPolicerOrch); - - gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); - gDirectory.set(gSwitchOrch); - ut_orch_list.push_back((Orch **)&gSwitchOrch); - - gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); - gDirectory.set(gNhgOrch); - ut_orch_list.push_back((Orch **)&gNhgOrch); - - vector srv6_tables = { - APP_SRV6_SID_LIST_TABLE_NAME, - APP_SRV6_MY_SID_TABLE_NAME - }; - gSrv6Orch = new Srv6Orch(m_app_db.get(), srv6_tables, gSwitchOrch, gVrfOrch, gNeighOrch); - gDirectory.set(gSrv6Orch); - ut_orch_list.push_back((Orch **)&gSrv6Orch); - gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); - gDirectory.set(gCrmOrch); - ut_orch_list.push_back((Orch **)&gCrmOrch); - - const int routeorch_pri = 5; - vector route_tables = { - { APP_ROUTE_TABLE_NAME, routeorch_pri }, - { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } - }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); - gDirectory.set(gRouteOrch); - ut_orch_list.push_back((Orch **)&gRouteOrch); - TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); - TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); - gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); - gDirectory.set(gMirrorOrch); - ut_orch_list.push_back((Orch **)&gMirrorOrch); - - TableConnector confDbAclTable(m_config_db.get(), CFG_ACL_TABLE_TABLE_NAME); - TableConnector confDbAclTableType(m_config_db.get(), CFG_ACL_TABLE_TYPE_TABLE_NAME); - TableConnector confDbAclRuleTable(m_config_db.get(), CFG_ACL_RULE_TABLE_NAME); - TableConnector appDbAclTable(m_app_db.get(), APP_ACL_TABLE_TABLE_NAME); - TableConnector appDbAclTableType(m_app_db.get(), APP_ACL_TABLE_TYPE_TABLE_NAME); - TableConnector appDbAclRuleTable(m_app_db.get(), APP_ACL_RULE_TABLE_NAME); - - vector acl_table_connectors = { - confDbAclTableType, - confDbAclTable, - confDbAclRuleTable, - appDbAclTable, - appDbAclRuleTable, - appDbAclTableType, - }; - gAclOrch = new AclOrch(acl_table_connectors, m_state_db.get(), - gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, NULL); - gDirectory.set(gAclOrch); - ut_orch_list.push_back((Orch **)&gAclOrch); - - m_MuxOrch = new MuxOrch(m_config_db.get(), mux_tables, m_TunnelDecapOrch, gNeighOrch, gFdbOrch); - gDirectory.set(m_MuxOrch); - ut_orch_list.push_back((Orch **)&m_MuxOrch); - - m_MuxCableOrch = new MuxCableOrch(m_app_db.get(), m_state_db.get(), APP_MUX_CABLE_TABLE_NAME); - gDirectory.set(m_MuxCableOrch); - ut_orch_list.push_back((Orch **)&m_MuxCableOrch); - - m_MuxStateOrch = new MuxStateOrch(m_state_db.get(), STATE_HW_MUX_CABLE_TABLE_NAME); - gDirectory.set(m_MuxStateOrch); - ut_orch_list.push_back((Orch **)&m_MuxStateOrch); - - ApplyDualTorConfigs(); INIT_SAI_API_MOCK(neighbor); INIT_SAI_API_MOCK(route); INIT_SAI_API_MOCK(acl); @@ -370,19 +127,9 @@ namespace mux_rollback_test MockSaiApis(); } - void TearDown() override + void PreTearDown() override { - for (std::vector::reverse_iterator rit = ut_orch_list.rbegin(); rit != ut_orch_list.rend(); ++rit) - { - Orch **orch = *rit; - delete *orch; - *orch = nullptr; - } - - gDirectory.m_values.clear(); - RestoreSaiApis(); - ut_helper::uninitSaiApi(); } }; @@ -390,110 +137,110 @@ namespace mux_rollback_test { EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyNeighborNotFound) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveRouteNotFound) { EXPECT_CALL(*mock_sai_route_api, remove_route_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyRouteAlreadyExists) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_route_api, create_route_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveAclNotFound) { EXPECT_CALL(*mock_sai_acl_api, remove_acl_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyAclAlreadyExists) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_acl_api, create_acl_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveNextHopAlreadyExists) { EXPECT_CALL(*mock_sai_next_hop_api, create_next_hop) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyNextHopNotFound) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_next_hop_api, remove_next_hop) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveRuntimeErrorRollbackToStandby) { EXPECT_CALL(*mock_sai_route_api, remove_route_entry) .WillOnce(Throw(runtime_error("Mock runtime error"))); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyRuntimeErrorRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_route_api, create_route_entry) .WillOnce(Throw(runtime_error("Mock runtime error"))); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, StandbyToActiveLogicErrorRollbackToStandby) { EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry) .WillOnce(Throw(logic_error("Mock logic error"))); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyLogicErrorRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry) .WillOnce(Throw(logic_error("Mock logic error"))); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, StandbyToActiveExceptionRollbackToStandby) { EXPECT_CALL(*mock_sai_next_hop_api, create_next_hop) .WillOnce(Throw(exception())); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyExceptionRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_next_hop_api, remove_next_hop) .WillOnce(Throw(exception())); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } -} +} \ No newline at end of file diff --git a/tests/mock_tests/neighorch_ut.cpp b/tests/mock_tests/neighorch_ut.cpp new file mode 100644 index 0000000000..03957436a6 --- /dev/null +++ b/tests/mock_tests/neighorch_ut.cpp @@ -0,0 +1,198 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_sai_api.h" +#include "mock_orch_test.h" + + +EXTERN_MOCK_FNS + +namespace neighorch_test +{ + DEFINE_SAI_API_MOCK(neighbor); + using namespace std; + using namespace mock_orch_test; + using ::testing::Return; + using ::testing::Throw; + + static const string TEST_IP = "10.10.10.10"; + static const NeighborEntry VLAN1000_NEIGH = NeighborEntry(TEST_IP, VLAN_1000); + static const NeighborEntry VLAN2000_NEIGH = NeighborEntry(TEST_IP, VLAN_2000); + + class NeighOrchTest: public MockOrchTest + { + protected: + void SetAndAssertMuxState(std::string interface, std::string state) + { + MuxCable* muxCable = m_MuxOrch->getMuxCable(interface); + muxCable->setState(state); + EXPECT_EQ(state, muxCable->getState()); + } + + void LearnNeighbor(std::string vlan, std::string ip, std::string mac) + { + Table neigh_table = Table(m_app_db.get(), APP_NEIGH_TABLE_NAME); + string key = vlan + neigh_table.getTableNameSeparator() + ip; + neigh_table.set(key, { { "neigh", mac }, { "family", "IPv4" } }); + gNeighOrch->addExistingData(&neigh_table); + static_cast(gNeighOrch)->doTask(); + neigh_table.del(key); + } + + void ApplyInitialConfigs() + { + Table peer_switch_table = Table(m_config_db.get(), CFG_PEER_SWITCH_TABLE_NAME); + Table tunnel_table = Table(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); + Table mux_cable_table = Table(m_config_db.get(), CFG_MUX_CABLE_TABLE_NAME); + Table port_table = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table vlan_table = Table(m_app_db.get(), APP_VLAN_TABLE_NAME); + Table vlan_member_table = Table(m_app_db.get(), APP_VLAN_MEMBER_TABLE_NAME); + Table neigh_table = Table(m_app_db.get(), APP_NEIGH_TABLE_NAME); + Table intf_table = Table(m_app_db.get(), APP_INTF_TABLE_NAME); + Table fdb_table = Table(m_app_db.get(), APP_FDB_TABLE_NAME); + + auto ports = ut_helper::getInitialSaiPorts(); + port_table.set(ACTIVE_INTERFACE, ports[ACTIVE_INTERFACE]); + port_table.set(STANDBY_INTERFACE, ports[STANDBY_INTERFACE]); + port_table.set("PortConfigDone", { { "count", to_string(1) } }); + port_table.set("PortInitDone", { {} }); + + vlan_table.set(VLAN_1000, { { "admin_status", "up" }, + { "mtu", "9100" }, + { "mac", "00:aa:bb:cc:dd:ee" } }); + vlan_table.set(VLAN_2000, { { "admin_status", "up"}, + { "mtu", "9100" }, + { "mac", "aa:11:bb:22:cc:33" } }); + vlan_member_table.set( + VLAN_1000 + vlan_member_table.getTableNameSeparator() + ACTIVE_INTERFACE, + { { "tagging_mode", "untagged" } }); + + vlan_member_table.set( + VLAN_2000 + vlan_member_table.getTableNameSeparator() + STANDBY_INTERFACE, + { { "tagging_mode", "untagged" } }); + + intf_table.set(VLAN_1000, { { "grat_arp", "enabled" }, + { "proxy_arp", "enabled" }, + { "mac_addr", "00:00:00:00:00:00" } }); + + intf_table.set(VLAN_2000, { { "grat_arp", "enabled" }, + { "proxy_arp", "enabled" }, + { "mac_addr", "00:00:00:00:00:00" } }); + + intf_table.set( + VLAN_1000 + neigh_table.getTableNameSeparator() + "192.168.0.1/24", { + { "scope", "global" }, + { "family", "IPv4" }, + }); + + intf_table.set( + VLAN_2000 + neigh_table.getTableNameSeparator() + "192.168.2.1/24", { + { "scope", "global" }, + { "family", "IPv4" }, + }); + tunnel_table.set(MUX_TUNNEL, { { "dscp_mode", "uniform" }, + { "dst_ip", "2.2.2.2" }, + { "ecn_mode", "copy_from_outer" }, + { "encap_ecn_mode", "standard" }, + { "ttl_mode", "pipe" }, + { "tunnel_type", "IPINIP" } }); + + peer_switch_table.set(PEER_SWITCH_HOSTNAME, { { "address_ipv4", PEER_IPV4_ADDRESS } }); + + mux_cable_table.set(ACTIVE_INTERFACE, { { "server_ipv4", SERVER_IP1 + "/32" }, + { "server_ipv6", "a::a/128" }, + { "state", "auto" } }); + + mux_cable_table.set(STANDBY_INTERFACE, { { "server_ipv4", SERVER_IP2+ "/32" }, + { "server_ipv6", "a::b/128" }, + { "state", "auto" } }); + + gPortsOrch->addExistingData(&port_table); + gPortsOrch->addExistingData(&vlan_table); + gPortsOrch->addExistingData(&vlan_member_table); + static_cast(gPortsOrch)->doTask(); + + gIntfsOrch->addExistingData(&intf_table); + static_cast(gIntfsOrch)->doTask(); + + m_TunnelDecapOrch->addExistingData(&tunnel_table); + static_cast(m_TunnelDecapOrch)->doTask(); + + m_MuxOrch->addExistingData(&peer_switch_table); + static_cast(m_MuxOrch)->doTask(); + + m_MuxOrch->addExistingData(&mux_cable_table); + static_cast(m_MuxOrch)->doTask(); + + fdb_table.set( + VLAN_1000 + fdb_table.getTableNameSeparator() + MAC1, + { { "type", "dynamic" }, + { "port", ACTIVE_INTERFACE } }); + + fdb_table.set( + VLAN_2000 + fdb_table.getTableNameSeparator() + MAC2, + { { "type", "dynamic" }, + { "port", STANDBY_INTERFACE} }); + + fdb_table.set( + VLAN_1000 + fdb_table.getTableNameSeparator() + MAC3, + { { "type", "dynamic" }, + { "port", ACTIVE_INTERFACE} }); + + gFdbOrch->addExistingData(&fdb_table); + static_cast(gFdbOrch)->doTask(); + + SetAndAssertMuxState(ACTIVE_INTERFACE, ACTIVE_STATE); + SetAndAssertMuxState(STANDBY_INTERFACE, STANDBY_STATE); + } + + void PostSetUp() override + { + INIT_SAI_API_MOCK(neighbor); + MockSaiApis(); + } + + void PreTearDown() override + { + RestoreSaiApis(); + } + }; + + TEST_F(NeighOrchTest, MultiVlanIpLearning) + { + + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + + EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry); + LearnNeighbor(VLAN_2000, TEST_IP, MAC2); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 0); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 1); + + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC3); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 0); + } + + TEST_F(NeighOrchTest, MultiVlanUnableToRemoveNeighbor) + { + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + NextHopKey nexthop = { TEST_IP, VLAN_1000 }; + gNeighOrch->m_syncdNextHops[nexthop].ref_count = 1; + + EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry).Times(0); + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry).Times(0); + LearnNeighbor(VLAN_2000, TEST_IP, MAC2); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 0); + } +} diff --git a/tests/mock_tests/portal.h b/tests/mock_tests/portal.h index df73f65cc0..31fa4ac4b7 100644 --- a/tests/mock_tests/portal.h +++ b/tests/mock_tests/portal.h @@ -7,6 +7,7 @@ #include "crmorch.h" #include "copporch.h" #include "sfloworch.h" +#include "twamporch.h" #include "directory.h" #undef protected @@ -106,6 +107,19 @@ struct Portal } }; + struct TwampOrchInternal + { + static bool getTwampSessionStatus(TwampOrch &obj, const string &name, string& status) + { + return obj.getSessionStatus(name, status); + } + + static TwampStatsTable getTwampSessionStatistics(TwampOrch &obj) + { + return obj.m_twampStatistics; + } + }; + struct DirectoryInternal { template diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index fca4f34beb..96e421092e 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -68,6 +68,11 @@ namespace portsorch_test attr_list[0].value.s32 = _sai_port_fec_mode; status = SAI_STATUS_SUCCESS; } + else if (attr_count== 1 && attr_list[0].id == SAI_PORT_ATTR_OPER_STATUS) + { + attr_list[0].value.u32 = (uint32_t)SAI_PORT_OPER_STATUS_UP; + status = SAI_STATUS_SUCCESS; + } else { status = pold_sai_port_api->get_port_attribute(port_id, attr_count, attr_list); @@ -342,7 +347,12 @@ namespace portsorch_test gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; @@ -500,6 +510,73 @@ namespace portsorch_test }; + /* + * Test port flap count + */ + TEST_F(PortsOrchTest, PortFlapCount) + { + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // Populate port table with SAI ports + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone, PortInitDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + portTable.set("PortInitDone", { { "lanes", "0" } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + // Apply configuration : create ports + static_cast(gPortsOrch)->doTask(); + + // Get first port, expect the oper status is not UP + Port port; + gPortsOrch->getPort("Ethernet0", port); + ASSERT_TRUE(port.m_oper_status != SAI_PORT_OPER_STATUS_UP); + ASSERT_TRUE(port.m_flap_count == 0); + + auto exec = static_cast(gPortsOrch->getExecutor("PORT_STATUS_NOTIFICATIONS")); + auto consumer = exec->getNotificationConsumer(); + + // mock a redis reply for notification, it notifies that Ehernet0 is going to up + for (uint32_t count=0; count < 5; count++) { + sai_port_oper_status_t oper_status = (count % 2 == 0) ? SAI_PORT_OPER_STATUS_UP : SAI_PORT_OPER_STATUS_DOWN; + mockReply = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->type = REDIS_REPLY_ARRAY; + mockReply->elements = 3; // REDIS_PUBLISH_MESSAGE_ELEMNTS + mockReply->element = (redisReply **)calloc(sizeof(redisReply *), mockReply->elements); + mockReply->element[2] = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->element[2]->type = REDIS_REPLY_STRING; + sai_port_oper_status_notification_t port_oper_status; + port_oper_status.port_state = oper_status; + port_oper_status.port_id = port.m_port_id; + std::string data = sai_serialize_port_oper_status_ntf(1, &port_oper_status); + std::vector notifyValues; + FieldValueTuple opdata("port_state_change", data); + notifyValues.push_back(opdata); + std::string msg = swss::JSon::buildJson(notifyValues); + mockReply->element[2]->str = (char*)calloc(1, msg.length() + 1); + memcpy(mockReply->element[2]->str, msg.c_str(), msg.length()); + + // trigger the notification + consumer->readData(); + gPortsOrch->doTask(*consumer); + mockReply = nullptr; + + gPortsOrch->getPort("Ethernet0", port); + ASSERT_TRUE(port.m_oper_status == oper_status); + ASSERT_TRUE(port.m_flap_count == count+1); + } + + cleanupPorts(gPortsOrch); + } + TEST_F(PortsOrchTest, PortBulkCreateRemove) { auto portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); @@ -859,7 +936,6 @@ namespace portsorch_test std::deque kfvSerdes = {{ "Ethernet0", SET_COMMAND, { - { "admin_status", "up" }, { "idriver" , "0x6,0x6,0x6,0x6" } } }}; @@ -886,6 +962,35 @@ namespace portsorch_test ASSERT_EQ(_sai_set_admin_state_down_count, ++current_sai_api_call_count); ASSERT_EQ(_sai_set_admin_state_up_count, current_sai_api_call_count); + // Configure non-serdes attribute that does not trigger admin state change + std::deque kfvMtu = {{ + "Ethernet0", + SET_COMMAND, { + { "mtu", "1234" }, + } + }}; + + // Refill consumer + consumer->addToSync(kfvMtu); + + _hook_sai_port_api(); + current_sai_api_call_count = _sai_set_admin_state_down_count; + + // Apply configuration + static_cast(gPortsOrch)->doTask(); + + _unhook_sai_port_api(); + + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", p)); + ASSERT_TRUE(p.m_admin_state_up); + + // Verify mtu is set + ASSERT_EQ(p.m_mtu, 1234); + + // Verify no admin-disable then admin-enable + ASSERT_EQ(_sai_set_admin_state_down_count, current_sai_api_call_count); + ASSERT_EQ(_sai_set_admin_state_up_count, current_sai_api_call_count); + // Dump pending tasks std::vector taskList; gPortsOrch->dumpPendingTasks(taskList); @@ -1166,6 +1271,7 @@ namespace portsorch_test { _hook_sai_port_api(); Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table statePortTable = Table(m_state_db.get(), STATE_PORT_TABLE_NAME); std::deque entries; not_support_fetching_fec = false; @@ -1215,6 +1321,33 @@ namespace portsorch_test ASSERT_EQ(fec_mode, SAI_PORT_FEC_MODE_RS); + gPortsOrch->refreshPortStatus(); + std::vector values; + statePortTable.get("Ethernet0", values); + bool fec_found = false; + for (auto &valueTuple : values) + { + if (fvField(valueTuple) == "fec") + { + fec_found = true; + ASSERT_TRUE(fvValue(valueTuple) == "rs"); + } + } + ASSERT_TRUE(fec_found == true); + + /*Mock an invalid fec mode with high value*/ + _sai_port_fec_mode = 100; + gPortsOrch->refreshPortStatus(); + statePortTable.get("Ethernet0", values); + fec_found = false; + for (auto &valueTuple : values) + { + if (fvField(valueTuple) == "fec") + { + fec_found = true; + ASSERT_TRUE(fvValue(valueTuple) == "N/A"); + } + } mock_port_fec_modes = old_mock_port_fec_modes; _unhook_sai_port_api(); } @@ -1378,6 +1511,7 @@ namespace portsorch_test Table pgTable = Table(m_app_db.get(), APP_BUFFER_PG_TABLE_NAME); Table profileTable = Table(m_app_db.get(), APP_BUFFER_PROFILE_TABLE_NAME); Table poolTable = Table(m_app_db.get(), APP_BUFFER_POOL_TABLE_NAME); + Table transceieverInfoTable = Table(m_state_db.get(), STATE_TRANSCEIVER_INFO_TABLE_NAME); // Get SAI default ports to populate DB @@ -1411,6 +1545,7 @@ namespace portsorch_test for (const auto &it : ports) { portTable.set(it.first, it.second); + transceieverInfoTable.set(it.first, {}); } // Set PortConfigDone, PortInitDone @@ -1458,6 +1593,25 @@ namespace portsorch_test gBufferOrch->dumpPendingTasks(ts); ASSERT_TRUE(ts.empty()); + + // Verify port configuration + vector port_list; + port_list.resize(ports.size()); + sai_attribute_t attr; + sai_status_t status; + attr.id = SAI_SWITCH_ATTR_PORT_LIST; + attr.value.objlist.count = static_cast(port_list.size()); + attr.value.objlist.list = port_list.data(); + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + for (uint32_t i = 0; i < port_list.size(); i++) + { + attr.id = SAI_PORT_ATTR_HOST_TX_SIGNAL_ENABLE; + status = sai_port_api->get_port_attribute(port_list[i], 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + ASSERT_TRUE(attr.value.booldata); + } } TEST_F(PortsOrchTest, PfcDlrHandlerCallingDlrInitAttribute) @@ -1956,6 +2110,7 @@ namespace portsorch_test gPortsOrch->getPort("Ethernet0", port); ASSERT_TRUE(port.m_oper_status == SAI_PORT_OPER_STATUS_UP); + ASSERT_TRUE(port.m_flap_count == 1); std::vector values; portTable.get("Ethernet0", values); diff --git a/tests/mock_tests/qosorch_ut.cpp b/tests/mock_tests/qosorch_ut.cpp index 713238e9cd..7bd0b03276 100644 --- a/tests/mock_tests/qosorch_ut.cpp +++ b/tests/mock_tests/qosorch_ut.cpp @@ -406,8 +406,13 @@ namespace qosorch_test ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index 0bfe5ad073..1948f79c24 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -182,6 +182,7 @@ namespace routeorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); vector flex_counter_tables = { CFG_FLEX_COUNTER_TABLE_NAME @@ -197,9 +198,18 @@ namespace routeorch_test ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + gDirectory.set(gVrfOrch); + + EvpnNvoOrch *evpn_orch = new EvpnNvoOrch(m_app_db.get(), APP_VXLAN_EVPN_NVO_TABLE_NAME); + gDirectory.set(evpn_orch); ASSERT_EQ(gIntfsOrch, nullptr); - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + + vector intf_tables = { + { APP_INTF_TABLE_NAME, IntfsOrch::intfsorch_pri}, + { APP_SAG_TABLE_NAME, IntfsOrch::intfsorch_pri} + }; + gIntfsOrch = new IntfsOrch(m_app_db.get(), intf_tables, gVrfOrch, m_chassis_app_db.get()); const int fdborch_pri = 20; @@ -506,4 +516,32 @@ namespace routeorch_test gMockResponsePublisher.reset(); } + + TEST_F(RouteOrchTest, RouteOrchTestInvalidEvpnRoute) + { + std::deque entries; + entries.push_back({"Vrf1", "SET", { {"vni", "500100"}, {"v4", "true"}}}); + auto consumer = dynamic_cast(gVrfOrch->getExecutor(APP_VRF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gVrfOrch)->doTask(); + + entries.clear(); + entries.push_back({"Vrf1:1.1.1.0/24", "SET", { {"ifname", "Ethernet0,Ethernet0"}, + {"nexthop", "10.0.0.2,10.0.0.3"}, + {"vni_label", "500100"}, + {"router_mac", "7e:f0:c0:e4:b2:5a,7e:f0:c0:e4:b2:5b"}}}); + entries.push_back({"Vrf1:2.1.1.0/24", "SET", { {"ifname", "Ethernet0,Ethernet0"}, + {"nexthop", "10.0.0.2,10.0.0.3"}, + {"vni_label", "500100,500100"}, + {"router_mac", "7e:f0:c0:e4:b2:5b"}}}); + consumer = dynamic_cast(gRouteOrch->getExecutor(APP_ROUTE_TABLE_NAME)); + consumer->addToSync(entries); + + auto current_create_count = create_route_count; + auto current_set_count = set_route_count; + + static_cast(gRouteOrch)->doTask(); + ASSERT_EQ(current_create_count, create_route_count); + ASSERT_EQ(current_set_count, set_route_count); + } } diff --git a/tests/mock_tests/twamporch_ut.cpp b/tests/mock_tests/twamporch_ut.cpp new file mode 100644 index 0000000000..721950e74a --- /dev/null +++ b/tests/mock_tests/twamporch_ut.cpp @@ -0,0 +1,975 @@ +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_table.h" +#include "notifier.h" + +extern string gMySwitchType; + +extern sai_object_id_t gSwitchId; + +extern redisReply *mockReply; + + +namespace twamporch_test +{ + using namespace std; + + int create_twamp_session_count; + int set_twamp_session_count; + int remove_twamp_session_count; + + sai_twamp_api_t ut_sai_twamp_api; + sai_twamp_api_t *pold_sai_twamp_api; + sai_switch_api_t ut_sai_switch_api; + sai_switch_api_t *pold_sai_switch_api; + + sai_create_twamp_session_fn old_create_twamp_session; + sai_remove_twamp_session_fn old_remove_twamp_session; + sai_set_twamp_session_attribute_fn old_set_twamp_session_attribute; + + sai_status_t _ut_stub_sai_create_twamp_session( + _Out_ sai_object_id_t *twamp_session_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) + { + *twamp_session_id = (sai_object_id_t)(0x1); + create_twamp_session_count++; + return SAI_STATUS_SUCCESS; + } + + sai_status_t _ut_stub_sai_remove_twamp_session( + _In_ sai_object_id_t twamp_session_id) + { + remove_twamp_session_count++; + return SAI_STATUS_SUCCESS; + } + + sai_status_t _ut_stub_sai_set_twamp_session_attribute( + _In_ sai_object_id_t twamp_session_id, + _In_ const sai_attribute_t *attr) + { + set_twamp_session_count++; + if (attr->id == SAI_TWAMP_SESSION_ATTR_SESSION_ENABLE_TRANSMIT) + { + return SAI_STATUS_SUCCESS; + } + return old_set_twamp_session_attribute(twamp_session_id, attr); + } + + sai_status_t _ut_stub_sai_get_switch_attribute( + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) + { + if (attr_count == 1) + { + if (attr_list[0].id == SAI_SWITCH_ATTR_MAX_TWAMP_SESSION) + { + attr_list[0].value.u32 = 128; + return SAI_STATUS_SUCCESS; + } + } + return pold_sai_switch_api->get_switch_attribute(switch_id, attr_count, attr_list); + } + + sai_status_t _ut_stub_sai_set_switch_attribute( + _In_ sai_object_id_t switch_id, + _In_ const sai_attribute_t *attr) + { + if (attr[0].id == SAI_SWITCH_ATTR_TWAMP_SESSION_EVENT_NOTIFY) + { + return SAI_STATUS_SUCCESS; + } + return pold_sai_switch_api->set_switch_attribute(switch_id, attr); + } + + void _hook_sai_twamp_api() + { + ut_sai_twamp_api = *sai_twamp_api; + pold_sai_twamp_api = sai_twamp_api; + ut_sai_twamp_api.create_twamp_session = _ut_stub_sai_create_twamp_session; + ut_sai_twamp_api.remove_twamp_session = _ut_stub_sai_remove_twamp_session; + ut_sai_twamp_api.set_twamp_session_attribute = _ut_stub_sai_set_twamp_session_attribute; + sai_twamp_api = &ut_sai_twamp_api; + } + + void _unhook_sai_twamp_api() + { + sai_twamp_api = pold_sai_twamp_api; + } + + void _hook_sai_switch_api() + { + ut_sai_switch_api = *sai_switch_api; + pold_sai_switch_api = sai_switch_api; + ut_sai_switch_api.get_switch_attribute = _ut_stub_sai_get_switch_attribute; + ut_sai_switch_api.set_switch_attribute = _ut_stub_sai_set_switch_attribute; + sai_switch_api = &ut_sai_switch_api; + } + + void _unhook_sai_switch_api() + { + sai_switch_api = pold_sai_switch_api; + } + + class MockTwampOrch final + { + public: + MockTwampOrch() + { + this->confDb = std::make_shared("CONFIG_DB", 0); + TableConnector confDbTwampTable(this->confDb.get(), CFG_TWAMP_SESSION_TABLE_NAME); + TableConnector stateDbTwampTable(this->confDb.get(), STATE_TWAMP_SESSION_TABLE_NAME); + this->twampOrch = std::make_shared(confDbTwampTable, stateDbTwampTable, gSwitchOrch, gPortsOrch, gVrfOrch); + } + ~MockTwampOrch() = default; + + void doTwampTableTask(const std::deque &entries) + { + auto consumer = dynamic_cast((this->twampOrch.get())->getExecutor(CFG_TWAMP_SESSION_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(this->twampOrch.get())->doTask(*consumer); + } + + void doTwampNotificationTask() + { + auto exec = static_cast((this->twampOrch.get())->getExecutor("TWAMP_NOTIFICATIONS")); + auto consumer = exec->getNotificationConsumer(); + consumer->readData(); + static_cast(this->twampOrch.get())->doTask(*consumer); + } + + TwampOrch& get() + { + return *twampOrch; + } + + private: + std::shared_ptr confDb; + std::shared_ptr twampOrch; + }; + + class TwampOrchTest : public ::testing::Test + { + public: + TwampOrchTest() + { + this->initDb(); + } + virtual ~TwampOrchTest() = default; + + void SetUp() override + { + this->initSaiApi(); + this->initSwitch(); + this->initOrch(); + this->initPorts(); + _hook_sai_twamp_api(); + _hook_sai_switch_api(); + } + + void TearDown() override + { + this->deinitOrch(); + this->deinitSwitch(); + this->deinitSaiApi(); + _unhook_sai_twamp_api(); + _unhook_sai_switch_api(); + } + + private: + void initSaiApi() + { + std::map profileMap = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + auto status = ut_helper::initSaiApi(profileMap); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + void deinitSaiApi() + { + auto status = ut_helper::uninitSaiApi(); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + void initSwitch() + { + sai_status_t status; + sai_attribute_t attr; + + // Create switch + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + // Get switch default virtual router ID + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + } + + void deinitSwitch() + { + // Remove switch + auto status = sai_switch_api->remove_switch(gSwitchId); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gSwitchId = SAI_NULL_OBJECT_ID; + gVirtualRouterId = SAI_NULL_OBJECT_ID; + } + + void initOrch() + { + // + // SwitchOrch + // + TableConnector state_switch_table(this->stateDb.get(), "SWITCH_CAPABILITY"); + TableConnector app_switch_table(this->appDb.get(), APP_SWITCH_TABLE_NAME); + TableConnector conf_asic_sensors(this->configDb.get(), CFG_ASIC_SENSORS_TABLE_NAME); + + std::vector switchTableList = { + conf_asic_sensors, + app_switch_table + }; + + ASSERT_EQ(gSwitchOrch, nullptr); + gSwitchOrch = new SwitchOrch(this->appDb.get(), switchTableList, state_switch_table); + gDirectory.set(gSwitchOrch); + resourcesList.push_back(gSwitchOrch); + + // + // PortsOrch + // + const int portsorch_base_pri = 40; + + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + ASSERT_EQ(gPortsOrch, nullptr); + gPortsOrch = new PortsOrch(this->appDb.get(), this->stateDb.get(), ports_tables, this->chassisAppDb.get()); + gDirectory.set(gPortsOrch); + resourcesList.push_back(gPortsOrch); + + // + // VrfOrch + // + ASSERT_EQ(gVrfOrch, nullptr); + gVrfOrch = new VRFOrch(this->appDb.get(), APP_VRF_TABLE_NAME, this->stateDb.get(), STATE_VRF_OBJECT_TABLE_NAME); + resourcesList.push_back(gVrfOrch); + + + // + // BufferOrch + // + std::vector bufferTableList = { + APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME + }; + gBufferOrch = new BufferOrch(this->appDb.get(), this->configDb.get(), this->stateDb.get(), bufferTableList); + gDirectory.set(gBufferOrch); + resourcesList.push_back(gBufferOrch); + + // + // FlexCounterOrch + // + std::vector flexCounterTableList = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + + auto flexCounterOrch = new FlexCounterOrch(this->configDb.get(), flexCounterTableList); + gDirectory.set(flexCounterOrch); + resourcesList.push_back(flexCounterOrch); + + // + // CrmOrch + // + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(this->configDb.get(), CFG_CRM_TABLE_NAME); + gDirectory.set(gCrmOrch); + resourcesList.push_back(gCrmOrch); + } + + void deinitOrch() + { + std::reverse(resourcesList.begin(), resourcesList.end()); + for (auto &it : resourcesList) + { + delete it; + } + + gSwitchOrch = nullptr; + gPortsOrch = nullptr; + gVrfOrch = nullptr; + gBufferOrch = nullptr; + gCrmOrch = nullptr; + + Portal::DirectoryInternal::clear(gDirectory); + EXPECT_TRUE(Portal::DirectoryInternal::empty(gDirectory)); + } + + void initPorts() + { + auto portTable = Table(this->appDb.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // Populate port table with SAI ports + for (const auto &cit : ports) + { + portTable.set(cit.first, cit.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + // Set PortInitDone + portTable.set("PortInitDone", { { "lanes", "0" } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + } + + void initDb() + { + this->appDb = std::make_shared("APPL_DB", 0); + this->configDb = std::make_shared("CONFIG_DB", 0); + this->stateDb = std::make_shared("STATE_DB", 0); + this->countersDb = make_shared("COUNTERS_DB", 0); + this->chassisAppDb = make_shared("CHASSIS_APP_DB", 0); + this->asicDb = make_shared("ASIC_DB", 0); + } + + shared_ptr appDb; + shared_ptr configDb; + shared_ptr stateDb; + shared_ptr countersDb; + shared_ptr chassisAppDb; + shared_ptr asicDb; + + std::vector resourcesList; + }; + + TEST_F(TwampOrchTest, TwampOrchTestCreateDeleteSenderPacketCountSingle) + { + string twampSessionName = "TEST_SENDER1"; + + MockTwampOrch twampOrch; + + auto current_create_count = create_twamp_session_count; + auto current_remove_count = remove_twamp_session_count; + auto current_set_count = set_twamp_session_count; + + // Create TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"mode", "LIGHT" }, + {"role", "SENDER" }, + {"src_ip", "1.1.1.1" }, + {"src_udp_port", "862" }, + {"dst_ip", "2.2.2.2" }, + {"dst_udp_port", "863" }, + {"packet_count", "1000" }, + {"tx_interval", "10" }, + {"timeout", "10" }, + {"statistics_interval", "20000" }, + {"vrf_name", "default" }, + {"dscp", "0" }, + {"ttl", "10" }, + {"timestamp_format", "ntp" }, + {"padding_size", "100" }, + {"hw_lookup", "true" } + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "inactive"); + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + } + + // Start TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"admin_state", "enabled"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "active"); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + // Process Notification + { + // mock a redis reply for notification + mockReply = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->type = REDIS_REPLY_ARRAY; + mockReply->elements = 3; // REDIS_PUBLISH_MESSAGE_ELEMNTS + mockReply->element = (redisReply **)calloc(sizeof(redisReply *), mockReply->elements); + mockReply->element[2] = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->element[2]->type = REDIS_REPLY_STRING; + sai_twamp_session_event_notification_data_t twamp_session_data; + sai_twamp_session_stat_t counters_ids[SAI_TWAMP_SESSION_STAT_DURATION_TS]; + uint64_t counters[SAI_TWAMP_SESSION_STAT_DURATION_TS]; + twamp_session_data.session_state = SAI_TWAMP_SESSION_STATE_INACTIVE; + twamp_session_data.twamp_session_id = (sai_object_id_t)0x1; + twamp_session_data.session_stats.index = 1; + twamp_session_data.session_stats.number_of_counters = 11; + + counters_ids[0] = SAI_TWAMP_SESSION_STAT_RX_PACKETS; + counters_ids[1] = SAI_TWAMP_SESSION_STAT_RX_BYTE; + counters_ids[2] = SAI_TWAMP_SESSION_STAT_TX_PACKETS; + counters_ids[3] = SAI_TWAMP_SESSION_STAT_TX_BYTE; + counters_ids[4] = SAI_TWAMP_SESSION_STAT_DROP_PACKETS; + counters_ids[5] = SAI_TWAMP_SESSION_STAT_MAX_LATENCY; + counters_ids[6] = SAI_TWAMP_SESSION_STAT_MIN_LATENCY; + counters_ids[7] = SAI_TWAMP_SESSION_STAT_AVG_LATENCY; + counters_ids[8] = SAI_TWAMP_SESSION_STAT_MAX_JITTER; + counters_ids[9] = SAI_TWAMP_SESSION_STAT_MIN_JITTER; + counters_ids[10] = SAI_TWAMP_SESSION_STAT_AVG_JITTER; + counters[0] = 1000; + counters[1] = 100000; + counters[2] = 1000; + counters[3] = 100000; + counters[4] = 0; + counters[5] = 1987; + counters[6] = 1983; + counters[7] = 1984; + counters[8] = 2097; + counters[9] = 1896; + counters[10] = 1985; + twamp_session_data.session_stats.counters_ids = counters_ids; + twamp_session_data.session_stats.counters = counters; + + std::string data = sai_serialize_twamp_session_event_ntf(1, &twamp_session_data); + + std::vector notifyValues; + FieldValueTuple opdata("twamp_session_event", data); + notifyValues.push_back(opdata); + std::string msg = swss::JSon::buildJson(notifyValues); + mockReply->element[2]->str = (char*)calloc(1, msg.length() + 1); + memcpy(mockReply->element[2]->str, msg.c_str(), msg.length()); + + // trigger the notification + twampOrch.doTwampNotificationTask(); + mockReply = nullptr; + + TwampStatsTable twampStatistics = Portal::TwampOrchInternal::getTwampSessionStatistics(twampOrch.get()); + ASSERT_TRUE(twampStatistics.find(twampSessionName) != twampStatistics.end()); + ASSERT_EQ(twampStatistics[twampSessionName].rx_packets, 1000); + ASSERT_EQ(twampStatistics[twampSessionName].rx_bytes, 100000); + ASSERT_EQ(twampStatistics[twampSessionName].tx_packets, 1000); + ASSERT_EQ(twampStatistics[twampSessionName].tx_bytes, 100000); + ASSERT_EQ(twampStatistics[twampSessionName].drop_packets, 0); + ASSERT_EQ(twampStatistics[twampSessionName].max_latency, 1987); + ASSERT_EQ(twampStatistics[twampSessionName].min_latency, 1983); + ASSERT_EQ(twampStatistics[twampSessionName].avg_latency, 1984); + ASSERT_EQ(twampStatistics[twampSessionName].max_jitter, 2097); + ASSERT_EQ(twampStatistics[twampSessionName].min_jitter, 1896); + ASSERT_EQ(twampStatistics[twampSessionName].avg_jitter, 1985); + } + + // Delete TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + DEL_COMMAND, + { {} } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_FALSE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + } + + // Make sure both create and set has been called + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + TEST_F(TwampOrchTest, TwampOrchTestCreateDeleteSenderPacketCountMulti) + { + string twampSessionName = "TEST_SENDER1"; + + MockTwampOrch twampOrch; + + auto current_create_count = create_twamp_session_count; + auto current_remove_count = remove_twamp_session_count; + auto current_set_count = set_twamp_session_count; + + // Create TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"mode", "LIGHT" }, + {"role", "SENDER" }, + {"src_ip", "1.1.1.1" }, + {"src_udp_port", "1862" }, + {"dst_ip", "2.2.2.2" }, + {"dst_udp_port", "1863" }, + {"packet_count", "1000" }, + {"tx_interval", "10" }, + {"timeout", "10" }, + {"statistics_interval", "11000" } + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "inactive"); + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + } + + // Start TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"admin_state", "enabled"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "active"); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + // Process Notification + { + sai_twamp_session_event_notification_data_t twamp_session_data; + sai_twamp_session_stat_t counters_ids[SAI_TWAMP_SESSION_STAT_DURATION_TS]; + uint64_t counters[SAI_TWAMP_SESSION_STAT_DURATION_TS]; + uint64_t latency_total = 0; + uint64_t jitter_total = 0; + twamp_session_data.twamp_session_id = (sai_object_id_t)0x1; + twamp_session_data.session_stats.number_of_counters = 11; + counters_ids[0] = SAI_TWAMP_SESSION_STAT_RX_PACKETS; + counters_ids[1] = SAI_TWAMP_SESSION_STAT_RX_BYTE; + counters_ids[2] = SAI_TWAMP_SESSION_STAT_TX_PACKETS; + counters_ids[3] = SAI_TWAMP_SESSION_STAT_TX_BYTE; + counters_ids[4] = SAI_TWAMP_SESSION_STAT_DROP_PACKETS; + counters_ids[5] = SAI_TWAMP_SESSION_STAT_MAX_LATENCY; + counters_ids[6] = SAI_TWAMP_SESSION_STAT_MIN_LATENCY; + counters_ids[7] = SAI_TWAMP_SESSION_STAT_AVG_LATENCY; + counters_ids[8] = SAI_TWAMP_SESSION_STAT_MAX_JITTER; + counters_ids[9] = SAI_TWAMP_SESSION_STAT_MIN_JITTER; + counters_ids[10] = SAI_TWAMP_SESSION_STAT_AVG_JITTER; + twamp_session_data.session_stats.counters_ids = counters_ids; + twamp_session_data.session_stats.counters = counters; + for (uint8_t i = 1; i <= 10; i++) + { + // mock a redis reply for notification + mockReply = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->type = REDIS_REPLY_ARRAY; + mockReply->elements = 3; // REDIS_PUBLISH_MESSAGE_ELEMNTS + mockReply->element = (redisReply **)calloc(sizeof(redisReply *), mockReply->elements); + mockReply->element[2] = (redisReply *)calloc(sizeof(redisReply), 1); + mockReply->element[2]->type = REDIS_REPLY_STRING; + + twamp_session_data.session_state = (i<10) ? SAI_TWAMP_SESSION_STATE_ACTIVE : SAI_TWAMP_SESSION_STATE_INACTIVE; + twamp_session_data.session_stats.index = i; + counters[0] = 100; + counters[1] = 10000; + counters[2] = 100; + counters[3] = 10000; + counters[4] = 0; + counters[5] = 1000+i; + counters[6] = 1000+i; + counters[7] = 1000+i; + counters[8] = 1100+i; + counters[9] = 1100+i; + counters[10] = 1100+i; + latency_total += counters[7]; + jitter_total += counters[10]; + + std::string data = sai_serialize_twamp_session_event_ntf(1, &twamp_session_data); + + std::vector notifyValues; + FieldValueTuple opdata("twamp_session_event", data); + notifyValues.push_back(opdata); + std::string msg = swss::JSon::buildJson(notifyValues); + mockReply->element[2]->str = (char*)calloc(1, msg.length() + 1); + memcpy(mockReply->element[2]->str, msg.c_str(), msg.length()); + + // trigger the notification + twampOrch.doTwampNotificationTask(); + mockReply = nullptr; + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + if (i<10) + { + ASSERT_EQ(session_status, "active"); + } + else + { + ASSERT_EQ(session_status, "inactive"); + } + + TwampStatsTable twampStatistics = Portal::TwampOrchInternal::getTwampSessionStatistics(twampOrch.get()); + ASSERT_TRUE(twampStatistics.find(twampSessionName) != twampStatistics.end()); + ASSERT_EQ(twampStatistics[twampSessionName].rx_packets, 100*i); + ASSERT_EQ(twampStatistics[twampSessionName].rx_bytes, 10000*i); + ASSERT_EQ(twampStatistics[twampSessionName].tx_packets, 100*i); + ASSERT_EQ(twampStatistics[twampSessionName].tx_bytes, 10000*i); + ASSERT_EQ(twampStatistics[twampSessionName].drop_packets, 0); + ASSERT_EQ(twampStatistics[twampSessionName].max_latency, 1000+i); + ASSERT_EQ(twampStatistics[twampSessionName].min_latency, 1000+1); + ASSERT_EQ(twampStatistics[twampSessionName].avg_latency, latency_total/i); + ASSERT_EQ(twampStatistics[twampSessionName].max_jitter, 1100+i); + ASSERT_EQ(twampStatistics[twampSessionName].min_jitter, 1100+1); + ASSERT_EQ(twampStatistics[twampSessionName].avg_jitter, jitter_total/i); + } + } + + // Delete TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + DEL_COMMAND, + { {} } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_FALSE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + } + + // Make sure both create and set has been called + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + TEST_F(TwampOrchTest, TwampOrchTestCreateDeleteSenderContinuousSingle) + { + string twampSessionName = "TEST_SENDER1"; + + MockTwampOrch twampOrch; + + auto current_create_count = create_twamp_session_count; + auto current_remove_count = remove_twamp_session_count; + auto current_set_count = set_twamp_session_count; + + // Create TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"mode", "LIGHT" }, + {"role", "SENDER" }, + {"src_ip", "1.1.1.1" }, + {"src_udp_port", "862" }, + {"dst_ip", "2.2.2.2" }, + {"dst_udp_port", "863" }, + {"monitor_time", "60" }, + {"tx_interval", "100" }, + {"timeout", "10" }, + {"statistics_interval", "60000" }, + {"vrf_name", "default" }, + {"dscp", "0" }, + {"ttl", "10" }, + {"timestamp_format", "ntp" }, + {"padding_size", "100" }, + {"hw_lookup", "true" } + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "inactive"); + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + } + + // Start TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"admin_state", "enabled"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "active"); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + // Delete TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + DEL_COMMAND, + { {} } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_FALSE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + } + + // Make sure both create and set has been called + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + TEST_F(TwampOrchTest, TwampOrchTestCreateDeleteSenderContinuousMulti) + { + string twampSessionName = "TEST_SENDER1"; + + MockTwampOrch twampOrch; + + auto current_create_count = create_twamp_session_count; + auto current_remove_count = remove_twamp_session_count; + auto current_set_count = set_twamp_session_count; + + // Create TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"mode", "LIGHT" }, + {"role", "SENDER" }, + {"src_ip", "1.1.1.1" }, + {"src_udp_port", "1862" }, + {"dst_ip", "2.2.2.2" }, + {"dst_udp_port", "1863" }, + {"monitor_time", "0" }, + {"tx_interval", "100" }, + {"timeout", "10" }, + {"statistics_interval", "20000" }, + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "inactive"); + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + } + + // Start TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"admin_state", "enabled"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "active"); + ASSERT_EQ(current_set_count + 1, set_twamp_session_count); + } + + // Stop TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"admin_state", "disabled"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "inactive"); + ASSERT_EQ(current_set_count + 2, set_twamp_session_count); + } + + // Delete TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + DEL_COMMAND, + { {} } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_FALSE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + } + + // Make sure both create and set has been called + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + ASSERT_EQ(current_set_count + 2, set_twamp_session_count); + } + + TEST_F(TwampOrchTest, TwampOrchTestCreateDeleteReflector) + { + string twampSessionName = "TEST_SENDER1"; + + MockTwampOrch twampOrch; + + auto current_create_count = create_twamp_session_count; + auto current_remove_count = remove_twamp_session_count; + auto current_set_count = set_twamp_session_count; + + // Create TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + SET_COMMAND, + { + {"mode", "LIGHT"}, + {"role", "REFLECTOR"}, + {"src_ip", "1.1.1.1"}, + {"src_udp_port", "862"}, + {"dst_ip", "2.2.2.2"}, + {"dst_udp_port", "863"} + } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_TRUE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(session_status, "active"); + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + } + + // Delete TWAMP Light session + { + std::deque tableKofvt; + tableKofvt.push_back( + { + twampSessionName, + DEL_COMMAND, + { {} } + } + ); + + twampOrch.doTwampTableTask(tableKofvt); + + string session_status; + ASSERT_FALSE(twampOrch.get().getSessionStatus(twampSessionName, session_status)); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + } + + // Make sure both create and set has been called + ASSERT_EQ(current_create_count + 1, create_twamp_session_count); + ASSERT_EQ(current_remove_count + 1, remove_twamp_session_count); + ASSERT_EQ(current_set_count, set_twamp_session_count); + } +} \ No newline at end of file diff --git a/tests/mock_tests/ut_saihelper.cpp b/tests/mock_tests/ut_saihelper.cpp index 8b6b35b6f7..c9bed67691 100644 --- a/tests/mock_tests/ut_saihelper.cpp +++ b/tests/mock_tests/ut_saihelper.cpp @@ -89,6 +89,7 @@ namespace ut_helper sai_api_query(SAI_API_MPLS, (void**)&sai_mpls_api); sai_api_query(SAI_API_COUNTER, (void**)&sai_counter_api); sai_api_query(SAI_API_FDB, (void**)&sai_fdb_api); + sai_api_query(SAI_API_TWAMP, (void**)&sai_twamp_api); return SAI_STATUS_SUCCESS; } @@ -118,6 +119,7 @@ namespace ut_helper sai_buffer_api = nullptr; sai_queue_api = nullptr; sai_counter_api = nullptr; + sai_twamp_api = nullptr; return SAI_STATUS_SUCCESS; } diff --git a/tests/test_acl.py b/tests/test_acl.py index cf68d1516e..ed5789b2b0 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -1,5 +1,6 @@ import pytest from requests import request +import time L3_TABLE_TYPE = "L3" L3_TABLE_NAME = "L3_TEST" @@ -131,6 +132,38 @@ def test_InvalidAclRuleCreation(self, dvs_acl, l3_acl_table): dvs_acl.verify_acl_rule_status(L3_TABLE_NAME, "INVALID_RULE", None) dvs_acl.verify_no_acl_rules() + def test_AclRuleUpdate(self, dvs_acl, l3_acl_table): + """The test is to verify there is no duplicated flex counter when updating an ACL rule + """ + config_qualifiers = {"SRC_IP": "10.10.10.10/32"} + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP": dvs_acl.get_simple_qualifier_comparator("10.10.10.10&mask:255.255.255.255") + } + + dvs_acl.create_acl_rule(L3_TABLE_NAME, L3_RULE_NAME, config_qualifiers) + dvs_acl.verify_acl_rule(expected_sai_qualifiers) + + acl_rule_id = dvs_acl.get_acl_rule_id() + counter_id = dvs_acl.get_acl_counter_oid() + + new_config_qualifiers = {"SRC_IP": "10.10.10.11/32"} + new_expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP": dvs_acl.get_simple_qualifier_comparator("10.10.10.11&mask:255.255.255.255") + } + dvs_acl.update_acl_rule(L3_TABLE_NAME, L3_RULE_NAME, new_config_qualifiers) + # Verify the rule has been updated + retry = 5 + while dvs_acl.get_acl_rule_id() == acl_rule_id and retry >= 0: + retry -= 1 + time.sleep(1) + assert retry > 0 + dvs_acl.verify_acl_rule(new_expected_sai_qualifiers) + # Verify the previous counter is removed + if counter_id: + dvs_acl.check_acl_counter_not_in_counters_map(counter_id) + dvs_acl.remove_acl_rule(L3_TABLE_NAME, L3_RULE_NAME) + dvs_acl.verify_no_acl_rules() + def test_AclRuleL4SrcPort(self, dvs_acl, l3_acl_table): config_qualifiers = {"L4_SRC_PORT": "65000"} expected_sai_qualifiers = { diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 49d36b357c..1813ebf430 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -865,3 +865,41 @@ def test_bufferPortMaxParameter(self, dvs, testlog): dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) + + + def test_bufferPoolInitWithSHP(self, dvs, testlog): + self.setup_db(dvs) + + try: + # 1. Enable the shared headroom pool + default_lossless_buffer_parameter = self.config_db.get_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE') + default_lossless_buffer_parameter['over_subscribe_ratio'] = '2' + self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter) + + # 2. Stop the orchagent + _, oa_pid = dvs.runcmd("pgrep orchagent") + dvs.runcmd("kill -s SIGSTOP {}".format(oa_pid)) + + # 3. Remove the size from CONFIG_DB|BUFFER_POOL.ingress_lossless_pool + original_ingress_lossless_pool = self.config_db.get_entry('BUFFER_POOL', 'ingress_lossless_pool') + try: + self.config_db.delete_field('BUFFER_POOL', 'ingress_lossless_pool', 'size') + self.config_db.delete_field('BUFFER_POOL', 'ingress_lossless_pool', 'xoff') + except Exception as e: + pass + + # 4. Remove the ingress_lossless_pool from the APPL_DB + self.app_db.delete_entry('BUFFER_POOL_TABLE', 'ingress_lossless_pool') + + # 5. Mock it by adding a "TABLE_SET" entry to trigger the fallback logic + self.app_db.update_entry("BUFFER_PG_TABLE_SET", "", {"NULL": "NULL"}) + + # 6. Invoke the lua plugin + _, output = dvs.runcmd("redis-cli --eval /usr/share/swss/buffer_pool_vs.lua") + assert "ingress_lossless_pool:2048:1024" in output + + finally: + self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool', original_ingress_lossless_pool) + self.config_db.delete_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE') + self.app_db.delete_entry("BUFFER_PG_TABLE_SET", "") + dvs.runcmd("kill -s SIGCONT {}".format(oa_pid)) diff --git a/tests/test_buffer_traditional.py b/tests/test_buffer_traditional.py index 21371cb05a..5e1f26bd50 100644 --- a/tests/test_buffer_traditional.py +++ b/tests/test_buffer_traditional.py @@ -3,14 +3,18 @@ class TestBuffer(object): + from conftest import DockerVirtualSwitch lossless_pgs = [] INTF = "Ethernet0" def setup_db(self, dvs): - self.app_db = dvs.get_app_db() - self.asic_db = dvs.get_asic_db() - self.config_db = dvs.get_config_db() - self.counter_db = dvs.get_counters_db() + from conftest import ApplDbValidator, AsicDbValidator + from dvslib.dvs_database import DVSDatabase + + self.app_db: ApplDbValidator = dvs.get_app_db() + self.asic_db: AsicDbValidator = dvs.get_asic_db() + self.config_db: DVSDatabase = dvs.get_config_db() + self.counter_db: DVSDatabase = dvs.get_counters_db() # enable PG watermark self.set_pg_wm_status('enable') @@ -74,6 +78,10 @@ def get_pg_name_map(self): pg_name = "{}:{}".format(self.INTF, pg) pg_name_map[pg_name] = self.get_pg_oid(pg_name) return pg_name_map + + def check_syslog(self, dvs, marker, err_log, expected_cnt=1): + (exitcode, num) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep \"%s\" | wc -l" % (marker, err_log)]) + assert num.strip() >= str(expected_cnt) @pytest.fixture def setup_teardown_test(self, dvs): @@ -246,3 +254,102 @@ def test_buffer_pg_update(self, dvs, setup_teardown_test): dvs.port_field_set(extra_port, "speed", orig_speed) dvs.port_admin_set(self.INTF, "down") dvs.port_admin_set(extra_port, "down") + + def test_no_pg_profile_for_speed_and_length(self, dvs: DockerVirtualSwitch, setup_teardown_test): + """ + Test to verify that buffermgrd correctly handles a scenario where no PG profile + is configured for a given speed (10000) and cable length (80m) for Ethernet0 (self.INTF). + """ + orig_cable_len = None + orig_port_speed = None + orig_port_status = None + orig_port_qos_map = None + + test_cable_len = "80m" # cable length must not exist for test_speed in + test_speed = "10000" + test_port_status ="down" # can be up or down, but it must exist in port configuration + test_port_pfc_enable = "3,4" # does not matter, but must exist + + try: + ################################## + ## Save original configurations ## + ################################## + + # Save original cable length + fvs_cable_len = self.config_db.get_entry("CABLE_LENGTH", "AZURE") + orig_cable_len = fvs_cable_len.get(self.INTF) if fvs_cable_len else None + + # Save original port speed and admin status + fvs_port = self.config_db.get_entry("PORT", self.INTF) + orig_port_speed = fvs_port.get("speed") if fvs_port else None + orig_port_status = fvs_port.get("admin_status") if fvs_port else None + + # Save original port qos map + fvs_qos_map = self.config_db.get_entry("PORT_QOS_MAP", self.INTF) + orig_cable_len = fvs_qos_map.get("pfc_enable") if fvs_qos_map else None + + ###################################### + ## Send configurations to CONFIG_DB ## + ###################################### + + # Configure cable length + self.change_cable_len(test_cable_len) + + # Configure port speed + dvs.port_field_set(self.INTF, "speed", test_speed) + + # Configure PFC enable + self.set_port_qos_table(self.INTF, test_port_pfc_enable) + + # Add marker to log to make syslog verification easier + # Set before setting admin status to not miss syslog + marker = dvs.add_log_marker() + + # Configure admin status + dvs.port_admin_set(self.INTF, test_port_status) + + # Wait for buffermgrd to process the changes + time.sleep(2) + + ################## + ## Verification ## + ################## + + + # Check syslog if this error is present. This is expected. + self.check_syslog(dvs, marker, "Failed to process invalid entry, drop it") + + finally: + ############################### + ## Revert to original values ## + ############################### + + # Revert values to original values + # If there are none, then assume entry/field never existed and should be deleted + + # Revert cable length + if orig_cable_len: + self.change_cable_len(orig_cable_len) + else: + self.config_db.delete_entry("CABLE_LENGTH", "AZURE") + + # Revert port speed + if orig_port_speed: + dvs.port_field_set(self.INTF, "speed", orig_port_speed) + else: + self.config_db.delete_field("PORT", self.INTF, "speed") + + # Revert admin status + if orig_port_status: + dvs.port_admin_set(self.INTF, orig_port_status) + else: + self.config_db.delete_field("PORT", self.INTF, "admin_status") + + # Revert port qos map + if orig_port_qos_map: + self.config_db.update_entry("PORT_QOS_MAP", self.INTF, orig_port_qos_map) + else: + self.config_db.delete_entry("PORT_QOS_MAP", self.INTF) + + + diff --git a/tests/test_dash_vnet.py b/tests/test_dash_vnet.py index 031fd7a0ef..ec9c56b2a0 100644 --- a/tests/test_dash_vnet.py +++ b/tests/test_dash_vnet.py @@ -223,6 +223,17 @@ def test_eni(self, dvs): for fv in fvs.items(): if fv[0] == "SAI_ENI_ETHER_ADDRESS_MAP_ENTRY_ATTR_ENI_ID": assert fv[1] == str(self.eni_oid) + + # test admin state update + pb.admin_state = State.STATE_DISABLED + dashobj.create_eni(self.mac_string, {"pb": pb.SerializeToString()}) + time.sleep(3) + enis = dashobj.asic_eni_table.get_keys() + assert len(enis) == 1 + assert enis[0] == self.eni_oid + eni_attrs = dashobj.asic_eni_table[self.eni_oid] + assert eni_attrs["SAI_ENI_ATTR_ADMIN_STATE"] == "false" + return dashobj def test_vnet_map(self, dvs): diff --git a/tests/test_fabric.py b/tests/test_fabric.py index 2d1ea8c293..72ad828790 100644 --- a/tests/test_fabric.py +++ b/tests/test_fabric.py @@ -73,6 +73,14 @@ def test_voq_switch(self, vst): port_counters_stat_keys = flex_db.get_keys("FLEX_COUNTER_TABLE:" + meta_data['group_name']) for port_stat in port_counters_stat_keys: assert port_stat in dict(port_counters_keys.items()).values(), "Non port created on PORT_STAT_COUNTER group: {}".format(port_stat) + + # update some config_db entries + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tb = swsscommon.Table(cfg_db, "FABRIC_PORT") + fvs = swsscommon.FieldValuePairs([("isolateStatus","True")]) + tb.set("FABRIC_PORT|Fabric0", fvs ) + fvs = swsscommon.FieldValuePairs([("forceUnisolateStatus", "1")]) + tb.set("FABRIC_PORT|Fabric0", fvs ) else: print( "We do not check switch type:", cfg_switch_type ) diff --git a/tests/test_fabric_port_isolation.py b/tests/test_fabric_port_isolation.py new file mode 100644 index 0000000000..d92cb73fe1 --- /dev/null +++ b/tests/test_fabric_port_isolation.py @@ -0,0 +1,65 @@ +import random +from dvslib.dvs_database import DVSDatabase +from dvslib.dvs_common import PollingConfig + + +class TestVirtualChassis(object): + def test_voq_switch_fabric_link(self, vst): + """Test basic fabric link monitoring infrastructure in VOQ switchs. + + This test validates that fabric links get isolated if they experienced some errors. + And the link get unisolated if it clears the error for several consecutive polls. + """ + + dvss = vst.dvss + for name in dvss.keys(): + dvs = dvss[name] + # Get the config information and choose a linecard or fabric card to test. + config_db = dvs.get_config_db() + metatbl = config_db.get_entry("DEVICE_METADATA", "localhost") + + cfg_switch_type = metatbl.get("switch_type") + if cfg_switch_type == "fabric": + + # get state_db infor + sdb = dvs.get_state_db() + # key + port = "PORT1" + # There are 16 fabric ports in the test environment. + portNum = random.randint(1, 16) + port = "PORT"+str(portNum) + # wait for link monitoring algorithm skips init pollings + max_poll = PollingConfig(polling_interval=60, timeout=1200, strict=True) + if sdb.get_entry("FABRIC_PORT_TABLE", port)['STATUS'] == 'up': + sdb.wait_for_field_match("FABRIC_PORT_TABLE", port, {"SKIP_FEC_ERR_ON_LNKUP_CNT": "2"}, polling_config=max_poll) + try: + # clean up the system for the testing port. + # set TEST_CRC_ERRORS to 0 + # set TEST_CODE_ERRORS to 0 + # set TEST to "TEST" + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CRC_ERRORS":"0"}) + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CODE_ERRORS": "0"}) + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST": "TEST"}) + # inject testing errors and wait for link get isolated. + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CRC_ERRORS": "2"}) + sdb.wait_for_field_match("FABRIC_PORT_TABLE", port, {"AUTO_ISOLATED": "1"}, polling_config=max_poll) + + # clear the testing errors and wait for link get unisolated. + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CRC_ERRORS": "0"}) + sdb.wait_for_field_match("FABRIC_PORT_TABLE", port, {"AUTO_ISOLATED": "0"}, polling_config=max_poll) + finally: + # cleanup + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CRC_ERRORS": "0"}) + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST_CODE_ERRORS": "0"}) + sdb.update_entry("FABRIC_PORT_TABLE", port, {"TEST": "product"}) + else: + print("The link ", port, " is down") + else: + print("We do not check switch type:", cfg_switch_type) + + +# 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(): + pass + diff --git a/tests/test_sag.py b/tests/test_sag.py new file mode 100644 index 0000000000..075ec6371a --- /dev/null +++ b/tests/test_sag.py @@ -0,0 +1,329 @@ +import pytest +import time +import re +import json +import ipaddress +from swsscommon import swsscommon + +@pytest.mark.usefixtures('testlog') +class TestSag(object): + def setup_db(self, dvs): + dvs.setup_db() + self.app_db = dvs.get_app_db() + self.cfg_db = dvs.get_config_db() + self.asic_db = dvs.get_asic_db() + + def setup_interface(self, dvs, interface, vlan): + # bring up interface + dvs.set_interface_status(interface, "up") + + # create VLAN + vlan_intf = "Vlan{}".format(vlan) + dvs.create_vlan(vlan) + dvs.create_vlan_member(vlan, interface) + dvs.set_interface_status(vlan_intf, "up") + + def reset_interface(self, dvs, interface, vlan): + # remove VLAN + dvs.remove_vlan_member(vlan, interface) + dvs.remove_vlan(vlan) + + def create_vrf(self, vrf_name): + initial_entries = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER")) + self.cfg_db.create_entry(swsscommon.CFG_VRF_TABLE_NAME, vrf_name, {"empty": "empty"}) + time.sleep(1) + + current_entries = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER")) + assert len(current_entries - initial_entries) == 1 + return list(current_entries - initial_entries)[0] + + def remove_vrf(self, vrf_name): + self.cfg_db.delete_entry(swsscommon.CFG_VRF_TABLE_NAME, vrf_name) + time.sleep(1) + + def add_sag_mac(self, mac): + self.cfg_db.create_entry(swsscommon.CFG_SAG_TABLE_NAME, "GLOBAL", {"gateway_mac": mac}) + time.sleep(1) + + def remove_sag_mac(self): + self.cfg_db.delete_entry(swsscommon.CFG_SAG_TABLE_NAME, "GLOBAL") + time.sleep(1) + + def enable_sag(self, vlan): + vlan_intf = "Vlan{}".format(vlan) + self.cfg_db.update_entry(swsscommon.CFG_VLAN_INTF_TABLE_NAME, vlan_intf, {"static_anycast_gateway": "true"}) + time.sleep(1) + + def disable_sag(self, vlan): + vlan_intf = "Vlan{}".format(vlan) + self.cfg_db.update_entry(swsscommon.CFG_VLAN_INTF_TABLE_NAME, vlan_intf, {"static_anycast_gateway": "false"}) + time.sleep(1) + + def get_system_mac(self, dvs): + (exit_code, result) = dvs.runcmd(["sh", "-c", "ip link show eth0 | grep ether | awk '{print $2}'"]) + assert exit_code == 0 + return result.rstrip().lower() + + def get_asic_db_default_vrf_oid(self): + vrf_entries = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + assert len(vrf_entries) == 1 + return list(set(vrf_entries))[0] + return set(vrf_entries) + + def generate_ipv6_link_local_addr(self, mac, prefix_len): + eui64 = re.sub(r'[.:-]', '', mac).lower() + eui64 = eui64[0:6] + 'fffe' + eui64[6:] + eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:] + eui64_str = ':'.join(re.findall(r'.{4}', eui64)) + return ipaddress.IPv6Interface("fe80::{}/{}".format(eui64_str, str(prefix_len))) + + def check_kernel_intf_mac(self, dvs, interface, mac): + (exit_code, result) = dvs.runcmd(["sh", "-c", "ip link show {}".format(interface)]) + assert exit_code == 0 + assert mac in result + + def check_kernel_intf_ipv6_addr(self, dvs, interface, addr): + (exit_code, result) = dvs.runcmd(["sh", "-c", "ip -6 address show {}".format(interface)]) + assert exit_code == 0 + assert addr in result + + def check_app_db_sag_mac(self, fvs, mac): + assert fvs.get("gateway_mac") == mac + + def check_app_db_intf(self, fvs, mac, sag): + assert fvs.get("mac_addr") == mac and fvs.get("static_anycast_gateway") == sag + + def check_object_exist(self, db, table, expected_attributes): + keys = db.get_keys(table) + found = False + for key in keys: + fvs = db.get_entry(table, key) + found |= all(fvs.get(k).casefold() == v.casefold() for k, v in expected_attributes.items()) + if found: + break + assert found, f"Expected field/value pairs not found: expcted={expected_attributes}" + + def check_asic_db_router_interface(self, vlan_oid, mac, vrf_oid): + self.check_object_exist(self.asic_db, + "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE", + { + "SAI_ROUTER_INTERFACE_ATTR_TYPE": "SAI_ROUTER_INTERFACE_TYPE_VLAN", + "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": mac, + "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": vrf_oid, + "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": vlan_oid + } + ) + + def check_asic_db_route_entry(self, destination, vrf_oid, exist): + route_entries = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + dest_vrf = [(json.loads(route_entry)["dest"], json.loads(route_entry)["vr"]) + for route_entry in route_entries] + + if exist: + assert (destination, vrf_oid) in dest_vrf + else: + assert (destination, vrf_oid) not in dest_vrf + + + def test_SagAddRemove(self, dvs): + self.setup_db(dvs) + + default_mac = "00:00:00:00:00:00" + default_vrf_oid = self.get_asic_db_default_vrf_oid() + system_mac = self.get_system_mac(dvs) + + interface = "Ethernet0" + vlan = "100" + vlan_intf = "Vlan{}".format(vlan) + self.setup_interface(dvs, interface, vlan) + + ip = "1.1.1.1/24" + dvs.add_ip_address(vlan_intf, ip) + + # add SAG global MAC address + mac = "00:11:22:33:44:55" + self.add_sag_mac(mac) + fvs = dvs.get_app_db().wait_for_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + self.check_app_db_sag_mac(fvs, mac) + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=True) + + # enable SAG on the VLAN interface + self.enable_sag(vlan) + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"mac_addr": mac}) + + self.check_app_db_intf(fvs, mac, "true") + self.check_kernel_intf_mac(dvs, vlan_intf, mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), mac, default_vrf_oid) + + # disable SAG on the VLAN interface + self.disable_sag(vlan) + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"NULL": "NULL"} + ) + + self.check_app_db_intf(fvs, default_mac, "false") + self.check_kernel_intf_mac(dvs, vlan_intf, system_mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(system_mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), system_mac, default_vrf_oid) + + # delete SAG global MAC address + self.remove_sag_mac() + self.app_db.wait_for_deleted_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=False) + + # remove ip + dvs.remove_ip_address(vlan_intf, ip) + + # reset interface + self.reset_interface(dvs, interface, vlan) + + def test_SagRemoveWhenSagVlanEnabled(self, dvs): + self.setup_db(dvs) + + default_mac = "00:00:00:00:00:00" + default_vrf_oid = self.get_asic_db_default_vrf_oid() + system_mac = self.get_system_mac(dvs) + + interface = "Ethernet0" + vlan = "100" + vlan_intf = "Vlan{}".format(vlan) + self.setup_interface(dvs, interface, vlan) + + ip = "1.1.1.1/24" + dvs.add_ip_address(vlan_intf, ip) + + # add SAG global MAC address + mac = "00:11:22:33:44:55" + self.add_sag_mac(mac) + fvs = dvs.get_app_db().wait_for_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + self.check_app_db_sag_mac(fvs, mac) + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=True) + + # enable SAG on the VLAN interface + self.enable_sag(vlan) + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"mac_addr": mac}) + + self.check_app_db_intf(fvs, mac, "true") + self.check_kernel_intf_mac(dvs, vlan_intf, mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), mac, default_vrf_oid) + + # delete SAG global MAC address + self.remove_sag_mac() + self.app_db.wait_for_deleted_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=False) + + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"NULL": "NULL"} + ) + + self.check_app_db_intf(fvs, default_mac, "true") + self.check_kernel_intf_mac(dvs, vlan_intf, system_mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(system_mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), system_mac, default_vrf_oid) + + # remove ip + dvs.remove_ip_address(vlan_intf, ip) + + # reset interface + self.reset_interface(dvs, interface, vlan) + + + def test_SagAddRemoveInVrf(self, dvs): + self.setup_db(dvs) + + default_mac = "00:00:00:00:00:00" + default_vrf_oid = self.get_asic_db_default_vrf_oid() + system_mac = self.get_system_mac(dvs) + + interface = "Ethernet0" + vlan = "100" + vlan_intf = "Vlan{}".format(vlan) + self.setup_interface(dvs, interface, vlan) + + vrf_name = "Vrf1" + vrf_oid = self.create_vrf(vrf_name) + + ip = "1.1.1.1/24" + dvs.add_ip_address(vlan_intf, ip, vrf_name) + + # add SAG global MAC address + mac = "00:11:22:33:44:55" + self.add_sag_mac(mac) + fvs = dvs.get_app_db().wait_for_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + self.check_app_db_sag_mac(fvs, mac) + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=True) + self.check_asic_db_route_entry(str(ipv6_ll_route), vrf_oid, exist=True) + + # enable SAG on the VLAN interface + self.enable_sag(vlan) + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"mac_addr": mac, + "vrf_name": vrf_name}) + + self.check_app_db_intf(fvs, mac, "true") + self.check_kernel_intf_mac(dvs, vlan_intf, mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), mac, vrf_oid) + + # disable SAG on the VLAN interface + self.disable_sag(vlan) + fvs = self.app_db.wait_for_field_match( + swsscommon.APP_INTF_TABLE_NAME, + vlan_intf, + {"vrf_name": vrf_name} + ) + + self.check_app_db_intf(fvs, default_mac, "false") + self.check_kernel_intf_mac(dvs, vlan_intf, system_mac) + + ipv6_ll = self.generate_ipv6_link_local_addr(system_mac, 64) + self.check_kernel_intf_ipv6_addr(dvs, vlan_intf, str(ipv6_ll)) + self.check_asic_db_router_interface(dvs.getVlanOid(vlan), system_mac, vrf_oid) + + # delete SAG global MAC address + self.remove_sag_mac() + self.app_db.wait_for_deleted_entry(swsscommon.APP_SAG_TABLE_NAME, "GLOBAL") + + ipv6_ll_route = self.generate_ipv6_link_local_addr(mac, 128) + self.check_asic_db_route_entry(str(ipv6_ll_route), default_vrf_oid, exist=False) + self.check_asic_db_route_entry(str(ipv6_ll_route), vrf_oid, exist=False) + + # remove ip + dvs.remove_ip_address(vlan_intf, ip) + + # reset interface + self.reset_interface(dvs, interface, vlan) diff --git a/tests/test_twamp.py b/tests/test_twamp.py new file mode 100644 index 0000000000..d2d8edb8f0 --- /dev/null +++ b/tests/test_twamp.py @@ -0,0 +1,182 @@ +# This test suite covers the functionality of twamp light feature in SwSS +import pytest +import time + +@pytest.mark.usefixtures("testlog") +@pytest.mark.usefixtures('dvs_twamp_manager') +class TestTwampLight(object): + + def check_syslog(self, dvs, marker, log, expected_cnt): + (ec, out) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep \'%s\' | wc -l" % (marker, log)]) + assert out.strip() == str(expected_cnt) + + def test_SenderPacketCountSingle(self, dvs, testlog): + """ + This test covers the TWAMP Light session creation and removal operations + Operation flow: + 1. Create twamp-light session-sender using once packet-count + The session remains inactive + 2. Start twamp-light session + The session becomes active + 3. Remove twamp-light session + """ + + session = "TEST_SENDER1" + src_ip = "1.1.1.1" + src_udp_port = "862" + dst_ip = "2.2.2.2" + dst_udp_port = "863" + packet_count = "1000" + tx_interval = "10" + timeout = "10" + stats_interval = "20000" + + marker = dvs.add_log_marker() + + # create twamp-light session + self.dvs_twamp.create_twamp_light_session_sender_packet_count(session, src_ip, src_udp_port, dst_ip, dst_udp_port, packet_count, tx_interval, timeout) + + # start twamp-light session + self.dvs_twamp.start_twamp_light_sender(session) + + # wait for sending TWAMP-test done + time.sleep(12) + + # remove twamp-light session + self.dvs_twamp.remove_twamp_light_session(session) + self.dvs_twamp.verify_no_session() + + def test_SenderPacketCountMulti(self, dvs, testlog): + """ + This test covers the TWAMP Light Sender session creation and removal operations + Operation flow: + 1. Create twamp-light session-sender using multi packet-count + The session remains inactive + 2. Start twamp-light session + The session becomes active + 3. Remove twamp-light session + """ + + session = "TEST_SENDER1" + src_ip = "1.2.3.4" + src_udp_port = "862" + dst_ip = "5.6.7.8" + dst_udp_port = "863" + packet_count = "1000" + tx_interval = "10" + timeout = "10" + stats_interval = "11000" + + marker = dvs.add_log_marker() + + # create twamp-light session + self.dvs_twamp.create_twamp_light_session_sender_packet_count(session, src_ip, src_udp_port, dst_ip, dst_udp_port, packet_count, tx_interval, timeout, stats_interval) + + # start twamp-light session + self.dvs_twamp.start_twamp_light_sender(session) + + # wait for sending TWAMP-test done + time.sleep(120) + + # remove twamp-light session + self.dvs_twamp.remove_twamp_light_session(session) + self.dvs_twamp.verify_no_session() + + def test_SenderContinuousSingle(self, dvs, testlog): + """ + This test covers the TWAMP Light Sender session creation and removal operations + Operation flow: + 1. Create twamp-light session-sender using once continuous + The session remains inactive + 2. Start twamp-light session + The session becomes active + 3. Remove twamp-light session + """ + + session = "TEST_SENDER2" + src_ip = "11.11.11.11" + src_udp_port = "862" + dst_ip = "12.12.12.12" + dst_udp_port = "863" + monitor_time = "60" + tx_interval = "100" + timeout = "10" + stats_interval = "60000" + + marker = dvs.add_log_marker() + + # create twamp-light session + self.dvs_twamp.create_twamp_light_session_sender_continuous(session, src_ip, src_udp_port, dst_ip, dst_udp_port, monitor_time, tx_interval, timeout) + + # start twamp-light session + self.dvs_twamp.start_twamp_light_sender(session) + # wait for sending TWAMP-test done + time.sleep(60) + + # remove twamp-light session + self.dvs_twamp.remove_twamp_light_session(session) + self.dvs_twamp.verify_no_session() + + def test_SenderContinuousMulti(self, dvs, testlog): + """ + This test covers the continuous TWAMP Light Sender session creation and removal operations + Operation flow: + 1. Create twamp-light session-sender using multi continuous + The session remains inactive + 2. Start twamp-light session + The session becomes active + 3. Remove twamp-light session + """ + + session = "TEST_SENDER2" + src_ip = "11.12.13.14" + src_udp_port = "862" + dst_ip = "15.16.17.18" + dst_udp_port = "863" + monitor_time = "60" + tx_interval = "100" + timeout = "10" + stats_interval = "20000" + + marker = dvs.add_log_marker() + + # create twamp-light session + self.dvs_twamp.create_twamp_light_session_sender_continuous(session, src_ip, src_udp_port, dst_ip, dst_udp_port, monitor_time, tx_interval, timeout, stats_interval) + + # start twamp-light session + self.dvs_twamp.start_twamp_light_sender(session) + + # wait for sending TWAMP-test done + time.sleep(60) + + # remove twamp-light session + self.dvs_twamp.remove_twamp_light_session(session) + self.dvs_twamp.verify_no_session() + + def test_Reflector(self, dvs, testlog): + """ + This test covers the TWAMP Light Reflector session creation and removal operations + Operation flow: + 1. Create twamp-light session-reflector + 2. Remove twamp-light session + """ + + session = "TEST_REFLECTOR1" + src_ip = "22.1.1.1" + src_udp_port = "862" + dst_ip = "22.1.1.2" + dst_udp_port = "863" + + marker = dvs.add_log_marker() + + # create twamp-light session + self.dvs_twamp.create_twamp_light_session_reflector(session, src_ip, src_udp_port, dst_ip, dst_udp_port) + + # remove twamp-light session + self.dvs_twamp.remove_twamp_light_session(session) + self.dvs_twamp.verify_no_session() + +# 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(): + pass diff --git a/tests/test_virtual_chassis.py b/tests/test_virtual_chassis.py index c92ed88c40..5401f6870f 100644 --- a/tests/test_virtual_chassis.py +++ b/tests/test_virtual_chassis.py @@ -5,6 +5,8 @@ import pytest import buffer_model +DVS_ENV = ["ASIC_VENDOR=vs"] + class TestVirtualChassis(object): def set_lag_id_boundaries(self, vct): @@ -138,7 +140,6 @@ def test_voq_switch(self, vct): spcfg = ast.literal_eval(value) assert spcfg['count'] == sp_count, "Number of systems ports configured is invalid" - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_app_db_sync(self, vct): """Test chassis app db syncing. @@ -159,7 +160,6 @@ def test_chassis_app_db_sync(self, vct): keys = chassis_app_db.get_keys("SYSTEM_INTERFACE") assert len(keys), "No chassis app db syncing is done" - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_system_interface(self, vct): """Test RIF record creation in ASIC_DB for remote interfaces. @@ -216,7 +216,6 @@ def test_chassis_system_interface(self, vct): # Remote system ports's switch id should not match local switch id assert spcfginfo["attached_switch_id"] != lc_switch_id, "RIF system port with wrong switch_id" - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_system_neigh(self, vct): """Test neigh record create/delete and syncing to chassis app db. @@ -312,8 +311,8 @@ def chassis_system_neigh_create(): test_sysneigh = "" for sysnk in sysneighkeys: sysnk_tok = sysnk.split("|") - assert len(sysnk_tok) == 3, "Invalid system neigh key in chassis app db" - if sysnk_tok[2] == test_neigh_ip: + assert len(sysnk_tok) == 4, "Invalid system neigh key in chassis app db" + if sysnk_tok[3] == test_neigh_ip: test_sysneigh = sysnk break @@ -372,7 +371,7 @@ def chassis_system_neigh_create(): # Check for kernel entries _, output = dvs.runcmd("ip neigh show") - assert f"{test_neigh_ip} dev {inband_port}" in output, "Kernel neigh not found for remote neighbor" + assert f"{test_neigh_ip} dev {inband_port} lladdr {mac_address}" in output, "Kernel neigh not found for remote neighbor" _, output = dvs.runcmd("ip route show") assert f"{test_neigh_ip} dev {inband_port} scope link" in output, "Kernel route not found for remote neighbor" @@ -487,7 +486,6 @@ def chassis_system_neigh_create(): # Cleanup inband if configuration self.del_inbandif_port(vct, inband_port) - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_system_lag(self, vct): """Test PortChannel in VOQ based chassis systems. @@ -624,7 +622,6 @@ def test_chassis_system_lag(self, vct): break - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_system_lag_id_allocator_table_full(self, vct): """Test lag id allocator table full. @@ -702,7 +699,6 @@ def test_chassis_system_lag_id_allocator_table_full(self, vct): break - @pytest.mark.skip(reason="Failing. Under investigation") def test_chassis_system_lag_id_allocator_del_id(self, vct): """Test lag id allocator's release id and re-use id processing. diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 4873c072ed..c28d7cf320 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -794,6 +794,9 @@ def check_router_interface(self, dvs, intf_name, name, vlan_oid=0): expected_attr = { 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': 'SAI_VLAN_FLOOD_CONTROL_TYPE_NONE' } check_object(asic_db, self.ASIC_VLAN_TABLE, vlan_oid, expected_attr) + expected_attr = { 'SAI_VLAN_ATTR_UNKNOWN_MULTICAST_FLOOD_CONTROL_TYPE': 'SAI_VLAN_FLOOD_CONTROL_TYPE_NONE' } + check_object(asic_db, self.ASIC_VLAN_TABLE, vlan_oid, expected_attr) + check_linux_intf_arp_proxy(dvs, intf_name) self.rifs.add(new_rif) diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index 718aac9bb5..c0e4117f4b 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -1090,7 +1090,7 @@ def test_swss_port_state_syncup(self, dvs, testlog): orchStateCount += 1; # Only WARM_RESTART_TABLE|orchagent state=reconciled operation may exist after port oper status change. - assert orchStateCount == 1 + assert orchStateCount == 2 #clean up arp dvs.runcmd("arp -d 10.0.0.1") diff --git a/tests/virtual_chassis/8/default_config.json b/tests/virtual_chassis/8/default_config.json index 523ab8e450..6f77a1ade2 100644 --- a/tests/virtual_chassis/8/default_config.json +++ b/tests/virtual_chassis/8/default_config.json @@ -9,5 +9,111 @@ "start_chassis_db" : "1", "comment" : "default_config for a vs that runs chassis_db" } + }, + "FABRIC_MONITOR": { + "FABRIC_MONITOR_DATA": { + "monErrThreshCrcCells": "1", + "monErrThreshRxCells": "61035156", + "monPollThreshRecovery": "8", + "monPollThreshIsolation": "1" + } + }, + "FABRIC_PORT": { + "Fabric0": { + "alias": "Fabric0", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "0" + }, + "Fabric1": { + "alias": "Fabric1", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "1" + }, + "Fabric2": { + "alias": "Fabric2", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "2" + }, + "Fabric3": { + "alias": "Fabric3", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "3" + }, + "Fabric4": { + "alias": "Fabric4", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "4" + }, + "Fabric5": { + "alias": "Fabric5", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "5" + }, + "Fabric6": { + "alias": "Fabric6", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "6" + }, + "Fabric7": { + "alias": "Fabric7", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "7" + }, + "Fabric8": { + "alias": "Fabric8", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "8" + }, + "Fabric9": { + "alias": "Fabric9", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "9" + }, + "Fabric10": { + "alias": "Fabric10", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "10" + }, + "Fabric11": { + "alias": "Fabric11", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "11" + }, + "Fabric12": { + "alias": "Fabric12", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "12" + }, + "Fabric13": { + "alias": "Fabric13", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "13" + }, + "Fabric14": { + "alias": "Fabric14", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "14" + }, + "Fabric15": { + "alias": "Fabric15", + "isolateStatus": "False", + "forceUnisolateStatus": "0", + "lanes": "15" + } } }