From 007854ec00012912bed203021432f2f62710125d Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Fri, 15 Oct 2021 07:43:33 +0000 Subject: [PATCH 01/11] Add SAG support --- cfgmgr/intfmgr.cpp | 101 +++++++++++- cfgmgr/intfmgr.h | 5 +- cfgmgr/intfmgrd.cpp | 12 ++ orchagent/intfsorch.cpp | 120 ++++++++++++++- orchagent/intfsorch.h | 7 +- orchagent/orchdaemon.cpp | 9 +- orchagent/routeorch.cpp | 12 +- orchagent/routeorch.h | 2 +- orchagent/vrforch.cpp | 30 ++++ orchagent/vrforch.h | 11 ++ tests/mock_tests/aclorch_ut.cpp | 9 +- tests/test_sag.py | 264 ++++++++++++++++++++++++++++++++ 12 files changed, 559 insertions(+), 23 deletions(-) create mode 100644 tests/test_sag.py diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 3e0ed862be..f551489c4a 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -23,18 +23,22 @@ using namespace swss; #define LOOPBACK_DEFAULT_MTU_STR "65536" +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_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME), + m_appSagTableProducer(appDb, APP_SAG_TABLE_NAME) { if (!WarmStart::isWarmStart()) { @@ -477,6 +481,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string grat_arp = ""; string mpls = ""; string ipv6_link_local_mode = ""; + string sag = ""; for (auto idx : data) { @@ -515,6 +520,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { ipv6_link_local_mode = value; } + else if (field == "static_anycast_gateway") + { + sag = value; + } } if (op == SET_COMMAND) @@ -643,8 +652,55 @@ 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)) + { + vector fvs; + string gwmac = ""; + if (m_cfgSagTable.get("GLOBAL", fvs)) + { + for (auto idx: fvs) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + + if (field == "gwmac") + { + gwmac = value; + } + } + } + + if (!gwmac.empty()) + { + if (sag == "true") + { + setHostSubIntfAdminStatus(alias, "down"); + setIntfMac(alias, gwmac); + setHostSubIntfAdminStatus(alias, "up"); + + FieldValueTuple fvTuple("mac_addr", MacAddress(gwmac).to_string()); + data.push_back(fvTuple); + } + else if (sag == "false") + { + setHostSubIntfAdminStatus(alias, "down"); + setIntfMac(alias, gMacAddress.to_string()); + setHostSubIntfAdminStatus(alias, "up"); + + FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); + data.push_back(fvTuple); + } + } + } + } + else + { + FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); + data.push_back(fvTuple); + } } if (!proxy_arp.empty()) @@ -772,6 +828,37 @@ 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 == "gwmac") + { + mac = value; + } + } + + vector fvAppSag; + if (op == SET_COMMAND) + { + FieldValueTuple gwmac("gwmac", MacAddress(mac).to_string()); + fvAppSag.push_back(gwmac); + m_appSagTableProducer.set("GLOBAL", fvAppSag); + } + else if (op == DEL_COMMAND) + { + m_appSagTableProducer.del("GLOBAL"); + } +} + void IntfMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -799,6 +886,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++; diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 655fb4deeb..c46fc9504c 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -18,8 +18,8 @@ class IntfMgr : public Orch using Orch::doTask; private: - ProducerStateTable m_appIntfTableProducer; - Table m_cfgIntfTable, m_cfgVlanIntfTable, m_cfgLagIntfTable, m_cfgLoopbackIntfTable; + 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; std::set m_subIntfList; @@ -33,6 +33,7 @@ class IntfMgr : public Orch 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); bool isIntfStateOk(const std::string &alias); diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index d6ed18526e..bffd805884 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" @@ -31,6 +32,7 @@ ofstream gRecordOfs; string gRecordFile; /* Global database mutex */ mutex gDbMutex; +MacAddress gMacAddress; int main(int argc, char **argv) { @@ -48,6 +50,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); @@ -68,6 +71,15 @@ int main(int argc, char **argv) s.addSelectables(o->getSelectables()); } + Table table(&cfgDb, "DEVICE_METADATA"); + std::vector ovalues; + table.get("localhost", ovalues); + auto it = std::find_if(ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); + if (it == ovalues.end()) { + throw runtime_error("couldn't find MAC address of the device from config DB"); + } + gMacAddress = MacAddress(it->second); + SWSS_LOG_NOTICE("starting main loop"); while (true) { diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index b270a0c5e5..6496bebb88 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -37,7 +37,7 @@ extern NeighOrch *gNeighOrch; extern string gMySwitchType; extern int32_t gVoqMySwitchId; -const int intfsorch_pri = 35; +const int IntfsOrch::intfsorch_pri = 35; #define RIF_FLEX_STAT_COUNTER_POLL_MSECS "1000" #define UPDATE_MAPS_SEC 1 @@ -56,8 +56,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(); @@ -104,7 +104,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)); } @@ -449,6 +449,14 @@ 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; + 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); } @@ -617,7 +625,6 @@ void IntfsOrch::doTask(Consumer &consumer) } string table_name = consumer.getTableName(); - auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { @@ -642,7 +649,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(!isRemoteSystemPortIntf(alias)) { @@ -652,6 +659,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; @@ -898,6 +916,7 @@ void IntfsOrch::doTask(Consumer &consumer) /* 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)); /*port.m_rif_id is set in setIntf(), need get port again*/ @@ -918,6 +937,12 @@ void IntfsOrch::doTask(Consumer &consumer) { SWSS_LOG_NOTICE("Set router interface mac %s for port %s success", mac.to_string().c_str(), port.m_alias.c_str()); + + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) + { + SWSS_LOG_NOTICE("update router interface mac to %s", port.m_mac.to_string().c_str()); + m_syncdIntfses[alias].mac = mac; + } } } else @@ -926,6 +951,23 @@ void IntfsOrch::doTask(Consumer &consumer) mac.to_string().c_str(), alias.c_str()); } } + else + { + if (gPortsOrch->getPort(alias, port)) + { + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) + { + if (m_syncdIntfses[alias].mac != port.m_mac) + { + if (setRouterIntfsMac(port)) + { + SWSS_LOG_NOTICE("reset %s router interface mac to %s", alias.c_str(), port.m_mac.to_string().c_str()); + m_syncdIntfses[alias].mac = port.m_mac; + } + } + } + } + } if (!proxy_arp.empty()) { @@ -1031,6 +1073,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 == "gwmac") + { + 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::addRouterIntfs(sai_object_id_t vrf_id, Port &port) { SWSS_LOG_ENTER(); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 5605abf133..f3f347787a 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&); @@ -77,8 +79,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/orchdaemon.cpp b/orchagent/orchdaemon.cpp index a546ffdac1..b3ffd96602 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -143,7 +143,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; @@ -287,7 +292,7 @@ bool OrchDaemon::init() }; gMacsecOrch = new MACsecOrch(m_applDb, m_stateDb, macsec_app_tables, gPortsOrch); - + /* * The order of the orch list is important for state restore of warm start and * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 04907b3de9..00d099429a 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -124,7 +124,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()); @@ -138,12 +138,12 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, } -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}; @@ -1434,7 +1434,7 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) /* We get 3 return values from setFgNhg: * 1. success/failure: on addition/modification of nexthop group/members * 2. next_hop_id: passed as a param to fn, used for sai route creation - * 3. prevNhgWasFineGrained: passed as a param to fn, used to determine transitions + * 3. prevNhgWasFineGrained: passed as a param to fn, used to determine transitions * between regular and FG ECMP, this is an optimization to prevent multiple lookups */ if (!m_fgNhgOrch->setFgNhg(vrf_id, ipPrefix, nextHops, next_hop_id, prevNhgWasFineGrained)) { @@ -1629,11 +1629,11 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) if (curNhgIsFineGrained && prevNhgWasFineGrained) { - /* Don't change route entry if the route is previously fine grained and new nhg is also fine grained. + /* Don't change route entry if the route is previously fine grained and new nhg is also fine grained. * We already modifed sai nhg objs as part of setFgNhg to account for nhg change. */ object_statuses.emplace_back(SAI_STATUS_SUCCESS); } - else + else { route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; route_attr.value.oid = next_hop_id; diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index 20e79699d5..fd878a8e8d 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -154,7 +154,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); private: SwitchOrch *m_switchOrch; diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 19ca5c0fd8..7839a20724 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -12,6 +12,7 @@ #include "vrforch.h" #include "vxlanorch.h" #include "directory.h" +#include "routeorch.h" using namespace std; using namespace swss; @@ -101,6 +102,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; @@ -165,6 +182,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 195015fa08..b36bbd1fb1 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/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 4b1ce101ae..6b3db19a75 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -327,8 +327,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; @@ -337,7 +342,7 @@ namespace aclorch_test { 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); ASSERT_EQ(gFdbOrch, nullptr); diff --git a/tests/test_sag.py b/tests/test_sag.py new file mode 100644 index 0000000000..e894a0739c --- /dev/null +++ b/tests/test_sag.py @@ -0,0 +1,264 @@ +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", {"gwmac": 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("gwmac") == 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_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) From 75ddeea462f6220493471739a4431fbe4875bd08 Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Wed, 3 Nov 2021 02:16:51 +0000 Subject: [PATCH 02/11] Refine field name in CONFIG_DB according to yang model change --- cfgmgr/intfmgr.cpp | 6 +++--- orchagent/intfsorch.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index ac513e9a72..110c75246c 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -672,7 +672,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, const auto &field = fvField(idx); const auto &value = fvValue(idx); - if (field == "gwmac") + if (field == "gateway_mac") { gwmac = value; } @@ -846,7 +846,7 @@ void IntfMgr::doSagTask(const vector& keys, const auto &field = fvField(idx); const auto &value = fvValue(idx); - if (field == "gwmac") + if (field == "gateway_mac") { mac = value; } @@ -855,7 +855,7 @@ void IntfMgr::doSagTask(const vector& keys, vector fvAppSag; if (op == SET_COMMAND) { - FieldValueTuple gwmac("gwmac", MacAddress(mac).to_string()); + FieldValueTuple gwmac("gateway_mac", MacAddress(mac).to_string()); fvAppSag.push_back(gwmac); m_appSagTableProducer.set("GLOBAL", fvAppSag); } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 6496bebb88..f2ecf644f3 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -1083,7 +1083,7 @@ void IntfsOrch::doSagTask(vector data, const string& op) 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 == "gwmac") + if (field == "gateway_mac") { sag_mac_str = value; } From b708ae4004cd4f9d8cbb6f5c424c982f2dc132f5 Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Thu, 31 Mar 2022 05:53:53 +0000 Subject: [PATCH 03/11] Fix compile error and bugs * Fix change SAG mac doesn't reset MAC address on VLAN interface * Fix compile error for update admin status api change --- cfgmgr/intfmgr.cpp | 56 ++++++++++++++++++++++++++++--- cfgmgr/intfmgr.h | 1 + orchagent/intfsorch.cpp | 6 ++-- tests/mock_tests/portsorch_ut.cpp | 7 +++- tests/mock_tests/routeorch_ut.cpp | 7 +++- 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 1e00243b87..ddc6cc03c3 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -944,18 +944,18 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { if (sag == "true") { - setHostSubIntfAdminStatus(alias, "down"); + updateSubIntfAdminStatus(alias, "down"); setIntfMac(alias, gwmac); - setHostSubIntfAdminStatus(alias, "up"); + updateSubIntfAdminStatus(alias, "up"); FieldValueTuple fvTuple("mac_addr", MacAddress(gwmac).to_string()); data.push_back(fvTuple); } else if (sag == "false") { - setHostSubIntfAdminStatus(alias, "down"); + updateSubIntfAdminStatus(alias, "down"); setIntfMac(alias, gMacAddress.to_string()); - setHostSubIntfAdminStatus(alias, "up"); + updateSubIntfAdminStatus(alias, "up"); FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); data.push_back(fvTuple); @@ -1126,10 +1126,15 @@ void IntfMgr::doSagTask(const vector& keys, 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()); } } @@ -1165,7 +1170,7 @@ void IntfMgr::doTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } - + if (table_name == CFG_SAG_TABLE_NAME) { doSagTask(keys, data, op); @@ -1234,3 +1239,44 @@ 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; + } + + if (entryKeys.size() != 1) + { + continue; + } + + vector fvs; + m_cfgVlanIntfTable.get(key, fvs); + for (auto &fv: fvs) + { + std::string field = fvField(fv); + std::string value = fvValue(fv); + if (field == "static_anycast_gateway" && value == "true") + { + SWSS_LOG_NOTICE("set %s mac address to %s", key.c_str(), macAddr.c_str()); + + // enable SAG + updateSubIntfAdminStatus(key, "down"); + setIntfMac(key, macAddr); + updateSubIntfAdminStatus(key, "up"); + + vector vlanIntFv; + FieldValueTuple fvTuple("mac_addr", MacAddress(macAddr).to_string()); + vlanIntFv.push_back(fvTuple); + m_appIntfTableProducer.set(key, vlanIntFv); + } + } + } +} \ No newline at end of file diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 411b058cc2..c6f54e70ff 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -76,6 +76,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 m_replayDone {false}; }; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index d59f39f9ad..333c6e2e08 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -456,6 +456,8 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre else { intfs_entry.mac = gMacAddress; + port.m_mac = gMacAddress; + gPortsOrch->setPort(alias, port); } m_syncdIntfses[alias] = intfs_entry; m_vrfOrch->increaseVrfRefCount(vrf_id); @@ -955,7 +957,7 @@ void IntfsOrch::doTask(Consumer &consumer) if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) { - SWSS_LOG_NOTICE("update router interface mac to %s", port.m_mac.to_string().c_str()); + SWSS_LOG_NOTICE("update %s router interface mac to %s", alias.c_str(), mac.to_string().c_str()); m_syncdIntfses[alias].mac = mac; } } @@ -968,7 +970,7 @@ void IntfsOrch::doTask(Consumer &consumer) } else { - if (gPortsOrch->getPort(alias, port)) + if (!ip_prefix_in_key && gPortsOrch->getPort(alias, port)) { if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) { diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 28df6610fd..db6284121a 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -181,7 +181,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; diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index 84f92a088c..e1112db4ff 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -189,7 +189,12 @@ namespace routeorch_test gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); 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; From 4e4afb34ef253967057eb8b8501a7114673364d9 Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Wed, 14 Jun 2023 14:08:17 +0000 Subject: [PATCH 04/11] Fix compile error --- orchagent/intfsorch.cpp | 1 + tests/mock_tests/bufferorch_ut.cpp | 7 ++++++- tests/mock_tests/flowcounterrouteorch_ut.cpp | 9 +++++++-- tests/mock_tests/intfsorch_ut.cpp | 9 +++++++-- tests/mock_tests/mux_rollback_ut.cpp | 6 +++++- tests/mock_tests/qosorch_ut.cpp | 7 ++++++- 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 7accc4cdf5..48e2fea8d1 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -37,6 +37,7 @@ extern bool gIsNatSupported; extern NeighOrch *gNeighOrch; extern string gMySwitchType; extern int32_t gVoqMySwitchId; +extern RouteOrch *gRouteOrch; const int IntfsOrch::intfsorch_pri = 35; diff --git a/tests/mock_tests/bufferorch_ut.cpp b/tests/mock_tests/bufferorch_ut.cpp index 0ffb1997b5..b36ebb7246 100644 --- a/tests/mock_tests/bufferorch_ut.cpp +++ b/tests/mock_tests/bufferorch_ut.cpp @@ -217,8 +217,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/flowcounterrouteorch_ut.cpp b/tests/mock_tests/flowcounterrouteorch_ut.cpp index 10a52c6b05..cfb646d356 100644 --- a/tests/mock_tests/flowcounterrouteorch_ut.cpp +++ b/tests/mock_tests/flowcounterrouteorch_ut.cpp @@ -156,8 +156,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 +402,4 @@ namespace flowcounterrouteorch_test vrf_consumer->addToSync(entries); static_cast(gVrfOrch)->doTask(); } -} \ No newline at end of file +} diff --git a/tests/mock_tests/intfsorch_ut.cpp b/tests/mock_tests/intfsorch_ut.cpp index 60041520bd..27b7dff0e5 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/mux_rollback_ut.cpp b/tests/mock_tests/mux_rollback_ut.cpp index 578b6c817b..8ca9a83d0d 100644 --- a/tests/mock_tests/mux_rollback_ut.cpp +++ b/tests/mock_tests/mux_rollback_ut.cpp @@ -223,7 +223,11 @@ namespace mux_rollback_test 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()); + 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); 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; From 8548d1b08b9ba12d6fb88f3a5f7cb926e7d2afde Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Thu, 15 Jun 2023 06:43:29 +0000 Subject: [PATCH 05/11] Refine code and add comments --- cfgmgr/intfmgr.cpp | 37 +++++++++----------------- cfgmgr/intfmgrd.cpp | 10 +++---- orchagent/intfsorch.cpp | 59 +++++++++++------------------------------ 3 files changed, 33 insertions(+), 73 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index c2c06fa955..47f576d8f3 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -994,31 +994,19 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, // only VLAN interface can set static anycast gateway if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) { - vector fvs; string gwmac = ""; - if (m_cfgSagTable.get("GLOBAL", fvs)) - { - for (auto idx: fvs) - { - const auto &field = fvField(idx); - const auto &value = fvValue(idx); - - if (field == "gateway_mac") - { - gwmac = value; - } - } - } - - if (!gwmac.empty()) + if (m_cfgSagTable.hget("GLOBAL", "gateway_mac", gwmac)) { + // before change interface MAC, update sub-interface admin staus to down, + // it can prevent unwanted events received during interface changed in kernel + // bring it up after the changed if (sag == "true") { updateSubIntfAdminStatus(alias, "down"); setIntfMac(alias, gwmac); updateSubIntfAdminStatus(alias, "up"); - FieldValueTuple fvTuple("mac_addr", MacAddress(gwmac).to_string()); + FieldValueTuple fvTuple("mac_addr", gwmac); data.push_back(fvTuple); } else if (sag == "false") @@ -1029,6 +1017,8 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, 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()); } } } @@ -1322,18 +1312,17 @@ void IntfMgr::updateSagMac(const std::string &macAddr) continue; } + // only process the entry includes the SAG's config + // e.g. VLAN_INTERFACE|Vlan201 if (entryKeys.size() != 1) { continue; } - vector fvs; - m_cfgVlanIntfTable.get(key, fvs); - for (auto &fv: fvs) + string value = ""; + if (m_cfgVlanIntfTable.hget(key, "static_anycast_gateway", value)) { - std::string field = fvField(fv); - std::string value = fvValue(fv); - if (field == "static_anycast_gateway" && value == "true") + if (value == "true") { SWSS_LOG_NOTICE("set %s mac address to %s", key.c_str(), macAddr.c_str()); @@ -1343,7 +1332,7 @@ void IntfMgr::updateSagMac(const std::string &macAddr) updateSubIntfAdminStatus(key, "up"); vector vlanIntFv; - FieldValueTuple fvTuple("mac_addr", MacAddress(macAddr).to_string()); + FieldValueTuple fvTuple("mac_addr", macAddr); vlanIntFv.push_back(fvTuple); m_appIntfTableProducer.set(key, vlanIntFv); } diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index 41ab25d467..db2792d3f9 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -74,13 +74,13 @@ int main(int argc, char **argv) } Table table(&cfgDb, "DEVICE_METADATA"); - std::vector ovalues; - table.get("localhost", ovalues); - auto it = std::find_if(ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); - if (it == ovalues.end()) { + string mac = ""; + if (!table.hget("localhost", "mac", mac)) + { throw runtime_error("couldn't find MAC address of the device from config DB"); } - gMacAddress = MacAddress(it->second); + + gMacAddress = MacAddress(mac); SWSS_LOG_NOTICE("starting main loop"); while (true) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 48e2fea8d1..7237050413 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -489,6 +489,9 @@ 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; @@ -976,62 +979,30 @@ void IntfsOrch::doTask(Consumer &consumer) } } - if (mac) + // the interface MAC is updated by setIntf(), it can get from PortsOrch + if (mac && gPortsOrch->getPort(alias, port)) { - /* 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)); + memcpy(attr.value.mac, port.m_mac.getMac(), sizeof(sai_mac_t)); + sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); - /*port.m_rif_id is set in setIntf(), need get port again*/ - if (gPortsOrch->getPort(alias, port)) + if (status != SAI_STATUS_SUCCESS) { - sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); - if (status != SAI_STATUS_SUCCESS) - { - 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_ERROR("Failed to set router interface mac %s for port %s, rv:%d", + port.m_mac.to_string().c_str(), port.m_alias.c_str(), status); + if (handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status) == task_need_retry) { - SWSS_LOG_NOTICE("Set router interface mac %s for port %s success", - mac.to_string().c_str(), port.m_alias.c_str()); - - if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) - { - SWSS_LOG_NOTICE("update %s router interface mac to %s", alias.c_str(), mac.to_string().c_str()); - m_syncdIntfses[alias].mac = mac; - } + it++; + continue; } } - else - { - SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, getPort fail", - mac.to_string().c_str(), alias.c_str()); - } } else { - if (!ip_prefix_in_key && gPortsOrch->getPort(alias, port)) - { - if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) - { - if (m_syncdIntfses[alias].mac != port.m_mac) - { - if (setRouterIntfsMac(port)) - { - SWSS_LOG_NOTICE("reset %s router interface mac to %s", alias.c_str(), port.m_mac.to_string().c_str()); - m_syncdIntfses[alias].mac = port.m_mac; - } - } - } - } + 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()) From 371be653db7ceea2bb50245488b86760767fd9f1 Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Fri, 23 Jun 2023 16:46:59 +0000 Subject: [PATCH 06/11] Add delete SAG global MAC while VLAN is enabled test case * Fix test cases errors * Refine logic to keep it simple --- cfgmgr/intfmgr.cpp | 51 +++++++++++++++++++++++------- cfgmgr/intfmgr.h | 1 + orchagent/intfsorch.cpp | 56 +++++++++++++++++++++------------ tests/test_sag.py | 69 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 144 insertions(+), 33 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 47f576d8f3..99a1083ad6 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -198,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; @@ -997,23 +1018,21 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string gwmac = ""; if (m_cfgSagTable.hget("GLOBAL", "gateway_mac", gwmac)) { - // before change interface MAC, update sub-interface admin staus to down, - // it can prevent unwanted events received during interface changed in kernel - // bring it up after the changed + // before change interface MAC, set interface down and up to regenerate IPv6 LL by MAC if (sag == "true") { - updateSubIntfAdminStatus(alias, "down"); + setIntfState(alias, false); setIntfMac(alias, gwmac); - updateSubIntfAdminStatus(alias, "up"); + setIntfState(alias, true); FieldValueTuple fvTuple("mac_addr", gwmac); data.push_back(fvTuple); } else if (sag == "false") { - updateSubIntfAdminStatus(alias, "down"); + setIntfState(alias, false); setIntfMac(alias, gMacAddress.to_string()); - updateSubIntfAdminStatus(alias, "up"); + setIntfState(alias, true); FieldValueTuple fvTuple("mac_addr", MacAddress().to_string()); data.push_back(fvTuple); @@ -1326,16 +1345,26 @@ void IntfMgr::updateSagMac(const std::string &macAddr) { SWSS_LOG_NOTICE("set %s mac address to %s", key.c_str(), macAddr.c_str()); - // enable SAG - updateSubIntfAdminStatus(key, "down"); + // enable SAG, set device down and up to regenerate IPv6 LL by MAC + setIntfState(key, false); setIntfMac(key, macAddr); - updateSubIntfAdminStatus(key, "up"); + setIntfState(key, true); vector vlanIntFv; - FieldValueTuple fvTuple("mac_addr", macAddr); + + // 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()); } } } diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 70cc9ff234..21ff856b08 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -43,6 +43,7 @@ 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); diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 7237050413..16dfcfdd4a 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -499,8 +499,6 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre else { intfs_entry.mac = gMacAddress; - port.m_mac = gMacAddress; - gPortsOrch->setPort(alias, port); } m_syncdIntfses[alias] = intfs_entry; m_vrfOrch->increaseVrfRefCount(vrf_id); @@ -979,31 +977,49 @@ void IntfsOrch::doTask(Consumer &consumer) } } - // the interface MAC is updated by setIntf(), it can get from PortsOrch - if (mac && gPortsOrch->getPort(alias, port)) + if (!mac) { - sai_attribute_t attr; - attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS; - - memcpy(attr.value.mac, port.m_mac.getMac(), sizeof(sai_mac_t)); - sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr); + mac = gMacAddress; + } - if (status != SAI_STATUS_SUCCESS) + // update mac if it is changed + if (m_syncdIntfses.find(alias) != m_syncdIntfses.end()) + { + if (m_syncdIntfses[alias].mac != mac) { - SWSS_LOG_ERROR("Failed to set router interface mac %s for port %s, rv:%d", - port.m_mac.to_string().c_str(), port.m_alias.c_str(), status); - if (handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status) == task_need_retry) + // port.m_rif_id is set in setIntf(), need to get port again + if (gPortsOrch->getPort(alias, port)) { - it++; - continue; + 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) + { + 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_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()) { diff --git a/tests/test_sag.py b/tests/test_sag.py index e894a0739c..075ec6371a 100644 --- a/tests/test_sag.py +++ b/tests/test_sag.py @@ -42,7 +42,7 @@ def remove_vrf(self, vrf_name): time.sleep(1) def add_sag_mac(self, mac): - self.cfg_db.create_entry(swsscommon.CFG_SAG_TABLE_NAME, "GLOBAL", {"gwmac": mac}) + self.cfg_db.create_entry(swsscommon.CFG_SAG_TABLE_NAME, "GLOBAL", {"gateway_mac": mac}) time.sleep(1) def remove_sag_mac(self): @@ -88,7 +88,7 @@ def check_kernel_intf_ipv6_addr(self, dvs, interface, addr): assert addr in result def check_app_db_sag_mac(self, fvs, mac): - assert fvs.get("gwmac") == 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 @@ -191,6 +191,71 @@ def test_SagAddRemove(self, dvs): # 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) From 4a6d856ec8b595e137a31680d9abec057b103256 Mon Sep 17 00:00:00 2001 From: Jimi Chen Date: Fri, 26 Jan 2024 11:07:23 +0800 Subject: [PATCH 07/11] Add missing extern gMacAddress --- orchagent/vrforch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 7ab6c889ea..93785dfe1e 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -20,6 +20,7 @@ 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; From da1f6c04e6ff07e6fc941e92d857ea6682f80cc1 Mon Sep 17 00:00:00 2001 From: linarnan Date: Tue, 6 Feb 2024 16:35:13 +0000 Subject: [PATCH 08/11] Fixes mock test failure --- orchagent/p4orch/tests/Makefile.am | 3 +- orchagent/p4orch/tests/fake_routeorch.cpp | 232 ++++++++++++++++++++++ orchagent/p4orch/tests/test_main.cpp | 13 ++ 3 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 orchagent/p4orch/tests/fake_routeorch.cpp 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/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/test_main.cpp b/orchagent/p4orch/tests/test_main.cpp index 787e0622f4..3805f216bd 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; @@ -182,6 +186,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 +204,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; @@ -228,6 +234,13 @@ int main(int argc, char *argv[]) gCrmOrch = &crm_orch; VRFOrch vrf_orch(gAppDb, APP_VRF_TABLE_NAME, gStateDb, STATE_VRF_OBJECT_TABLE_NAME); gVrfOrch = &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(&vrf_orch)); FlowCounterRouteOrch flow_counter_route_orch(gConfigDb, std::vector{}); From 62a293dec47442e5e0fc3c6c431b2c4c124cb096 Mon Sep 17 00:00:00 2001 From: linarnan Date: Wed, 7 Feb 2024 09:44:45 +0800 Subject: [PATCH 09/11] Fixes mock run test failure (#2) * Fixes mock test failure * Fixes mock test run failure fixes pipeline run failure FAIL: p4orch_tests_usan ======================= ../../../orchagent/vrforch.cpp:113:41: runtime error: member call on null pointer of type 'struct RouteOrch' ../../../orchagent/vrforch.cpp:113:41: runtime error: member access within null pointer of type 'struct RouteOrch' FAIL p4orch_tests_usan (exit status: 139) --- orchagent/p4orch/tests/test_main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/orchagent/p4orch/tests/test_main.cpp b/orchagent/p4orch/tests/test_main.cpp index 3805f216bd..d768485826 100644 --- a/orchagent/p4orch/tests/test_main.cpp +++ b/orchagent/p4orch/tests/test_main.cpp @@ -243,6 +243,15 @@ int main(int argc, char *argv[]) gRouteOrch = &route_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)); + FlowCounterRouteOrch flow_counter_route_orch(gConfigDb, std::vector{}); gFlowCounterRouteOrch = &flow_counter_route_orch; gDirectory.set(static_cast(&flow_counter_route_orch)); From 69c34c385dd27ff77fddfdb155d0c0e52322af6a Mon Sep 17 00:00:00 2001 From: linarnan Date: Wed, 7 Feb 2024 02:10:23 +0000 Subject: [PATCH 10/11] Fixes merge conflict issue --- orchagent/p4orch/tests/test_main.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/orchagent/p4orch/tests/test_main.cpp b/orchagent/p4orch/tests/test_main.cpp index d768485826..8a92478add 100644 --- a/orchagent/p4orch/tests/test_main.cpp +++ b/orchagent/p4orch/tests/test_main.cpp @@ -234,13 +234,6 @@ int main(int argc, char *argv[]) gCrmOrch = &crm_orch; VRFOrch vrf_orch(gAppDb, APP_VRF_TABLE_NAME, gStateDb, STATE_VRF_OBJECT_TABLE_NAME); gVrfOrch = &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(&vrf_orch)); const int routeorch_pri = 5; From 97b3432aadde796967d9970e5eb1a44076f2a0c0 Mon Sep 17 00:00:00 2001 From: linarnan Date: Wed, 24 Apr 2024 21:26:17 +0800 Subject: [PATCH 11/11] Fixes sag merge conflicts (#3) * Fixes mock test failure * Fixes mock test run failure fixes pipeline run failure FAIL: p4orch_tests_usan ======================= ../../../orchagent/vrforch.cpp:113:41: runtime error: member call on null pointer of type 'struct RouteOrch' ../../../orchagent/vrforch.cpp:113:41: runtime error: member access within null pointer of type 'struct RouteOrch' FAIL p4orch_tests_usan (exit status: 139) * Fixed orchagent crash in VM with the Qos BUFFER_QUEUE|system-port|Queue-id-range config (#3050) * Fixed orchagent crash in VM with the Qos BUFFER_QUEUE|system-port|Queue-id-range config * [intfsorch] Enable ipv6 proxy ndp along with proxy arp (#3045) * [intfsorch] Enable ipv6 proxy ndp along with proxy arp setting SAI_VLAN_ATTR_UNKNOWN_MULTICAST_FLOOD_CONTROL_TYPE to SAI_VLAN_FLOOD_CONTROL_TYPE_NONE when proxy arp is enabled. This fixes a bug where ipv6 NS packets were flooding ports with duplicate packets. We now set multicast flood type to none. * Fix multi VLAN neighbor learning (#3049) What I did When adding a new neighbor, check if the neighbor IP has already been learned on a different VLAN. If it has, remove the old neighbor entry before adding the new one. Why I did it On Gemini devices, if a neighbor IP moves from an active port in one VLAN to a second VLAN, then back to the first VLAN (with 3 different MAC addresses), orchagent will crash. Even though the MAC address of the last move is different from the first MAC address, orchagent believes the last MAC address to already be programmed in the hardware and tries to set an attribute of the entry which doesn't exist. * [asan] Disable the "maybe-uninitialized" warning when compiled with ASAN enabled. * Set HOST_TX_READY_NOTIFY attribute only after query capabilities(#3070) *Set HOST_TX_READY_NOTIFY attribute only after query capabilities * [EVPN] Skip EVPN routes with invalid VNI or router mac field (#3073) * Skip EVPN routes with invalid VNI or router mac field * Add port flap count and last flap timestamp to APPL_DB (#3052) * Add port flap count and last flap timestamp * Add basic fabric link monitoring counters and states handling. (#2988) * Add basic fabric link monitoring counters and states handling. * [Mellanox] Fix inconsistence in the shared headroom pool initialization (#3057) * Fix inconsistence in the shared headroom pool initialization * Why I did it During initialization, if SHP is enabled the buffer pool sizes, xoff have initialized to 0, which means SHP is disabled but the buffer profiles already indicate SHP later on the buffer pool sizes are updated with off 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 SHP enabled by setting a very small buffer pool and SHP sizes * [acl] Add IN_PORTS qualifier for L3 table (#3078) * Apply IN_PORTS qualifiier for L3 table Why I did it IN_PORTS qualifier was allowed for L3 table in 202012 release and below. Changes in #1982 removed that support leading to regression in some of our testcases. The following error was observed ERR swss#orchagent: :- validateAclRuleMatch: Match SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS in rule RULE_1 is not supported by table DATAACL * [bulker] add support for neighbor bulking (#2768) Adding support for sai_neighbor_api_t bulking in bulker.h * [buffermgrd] Move switch-statement outside of if-statement in BufferMgr::doTask (#3055) * [buffermgr] Moved switch statement outside of if-statmement in Buffermgr::doTask The switch statement which would normally erase buffer events was moved to be inside the if-statement which would only enter if the event is a SET event. This was introduced in commit e5329c39. This would cause an infinite loop, since non-set events would never be erased. The switch statement has now been moved to occur outside the if, allowing for non-set commands to be processed. * [portsorch] process only updated APP_DB fields when port is already created (#3025) * [portsorch] process only updated APP_DB fields when port is already created What I did Fixing an issue when setting some port attribute in APPL_DB triggers serdes parameters to be re-programmed with port toggling. Made portsorch to handle only those attributes that were pushed to APPL_DB, so that serdes programming happens only by xcvrd's request to do so. * [Copp]Refactor coppmgr tests (#3093) What I did Refactoring coppmgr mock tests Why I did it After migration to bookworm, coppmgr tests started failing due to the use of sudo commands. * Revert "[acl] Add IN_PORTS qualifier for L3 table (#3078)" (#3092) This reverts commit 9d4a3addacba4289b42ce82626b3bc54a6e8b0bd. *Revert "[acl] Add IN_PORTS qualifier for L3 table" * [orchagent] TWAMP Light orchagent implementation (#2927) * [orchagent] TWAMP Light orchagent implementation. (#2927) * What I did Implemented the TWAMP Light feature according to the SONiC TWAMP Light HLD(sonic-net/SONiC#1320). * Clang format change. (#3080) What I did This PR has no real code change. It is purely clang formatting. It only applies to the P4Orch codes. Commands that I run: find orchagent/p4orch -name *.h -o -name .cpp | xargs clang-format -i -style="{BasedOnStyle: Microsoft, DerivePointerAlignment: false}" find orchagent -name response_publisher -o -name return_code.h | xargs clang-format -i -style="{BasedOnStyle: Microsoft, DerivePointerAlignment: false}" * T2-VOQ-VS: Fix iBGP bringup issue (#3053) * Fix iBGP bringup issue T2-vswitch * On T2-VOQ chassis Emulation with multi-asic linecards, iBGP sessions dont come up. Related Issue: sonic-net/sonic-buildimage#18129 * [Fdbsyncd] Adding extern_learn flag with fdb entry so Kernel doesn't age out (#2985) * Adding extern_learn flag with fdb entry so that Kernel doesn't age out the MAC * [Fdbsyncd] Adding extern_learn flag with fdb entry so Kernel doesn't age out What I did extern_learn flag is added while programming the fdb entry into the Kernel. This will make sure that kernel doesn't age out the fdb entry. (#15004) How I did it A flag extern_learn will be passed while programing the fdb entry. (#15004) How to verify it Tested MAC add/del to the Kernel from the local FDB entry. (#15004) Signed-off-by: kishore.kunal@broadcom.com --------- Signed-off-by: kishore.kunal@broadcom.com Co-authored-by: Sudharsan Dhamal Gopalarathnam * Fix oper FEC retrieval after warmboot (#3100) Updating oper FEC status in state_db after warm-reboot as part of refresh port status call * [EVPN]Fix fpmsyncd crash when EVPN type5 is received with bgp fib suppression enabled (#3101) * [EVPN]Fix fpmsyncd crash when EVPN type5 is received with bgp fib suppression enabled * [portsorch] Handle TRANSCEIVER_INFO table on warm boot (#3087) * Add existing data from TRANSCEIVER_INFO table * Introduce a new role for DPU-NPU Interconnect Signed-off-by: Vivek Reddy Karri Co-authored-by: Sudharsan Dhamal Gopalarathnam * [p4orch] Clang format change. (#3096) What I did [p4orch] This PR has no real code change. It is purely clang formatting. It does the same as #3080. * [dash] fix ENI admin state update (#3081) * [dash] fix ENI admin state update * Add force option for fabric port unisolate command (#3089) What I did Add force option to the unisolate link command, so users can make the links not isolate if they want. depends on sonic-net/sonic-buildimage#18447 * [twamporch] Explicitly initialize local variable (#3115) What I did Explicitly initialized local variable. Why I did it We met below error message in sonic-buildimage armhf build (sonic-net/sonic-buildimage#18334) * Add bookworm build to the PR checkers (#3114) What I did Add a Bookworm build to the PR checkers. Also fix some Bookworm build errors that crept in. Why I did it Buildimage now builds swss for Bookworm, so the build needs to succeed. * [ACL] Remove flex counter when updating ACL rule (#3118) What I did This PR is to fix sonic-net/sonic-buildimage#18719 When ACL rule is created for the first time, a flex counter is created and registered. When the same ACL rule is being updated, the FlexCounter created before is not removed, and another FlexCounter is created and registered. Why I did it Fix the issue that FlexCounter is duplicated when updating existing ACL rule. --------- Signed-off-by: kishore.kunal@broadcom.com Signed-off-by: Vivek Reddy Karri Co-authored-by: saksarav-nokia Co-authored-by: Nikola Dancejic <26731235+Ndancejic@users.noreply.github.com> Co-authored-by: Lawrence Lee Co-authored-by: Oleksandr Ivantsiv Co-authored-by: noaOrMlnx <58519608+noaOrMlnx@users.noreply.github.com> Co-authored-by: Lior Avramov <73036155+liorghub@users.noreply.github.com> Co-authored-by: Prince George <45705344+prgeor@users.noreply.github.com> Co-authored-by: jfeng-arista <98421150+jfeng-arista@users.noreply.github.com> Co-authored-by: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Co-authored-by: Neetha John Co-authored-by: Amir Co-authored-by: Stepan Blyshchak <38952541+stepanblyschak@users.noreply.github.com> Co-authored-by: Sudharsan Dhamal Gopalarathnam Co-authored-by: xiaodong hu <32903206+huseratgithub@users.noreply.github.com> Co-authored-by: mint570 <70396898+mint570@users.noreply.github.com> Co-authored-by: Deepak Singhal <115033986+deepak-singhal0408@users.noreply.github.com> Co-authored-by: KISHORE KUNAL <64033340+kishorekunal01@users.noreply.github.com> Co-authored-by: Vivek Co-authored-by: Yakiv Huryk <62013282+Yakiv-Huryk@users.noreply.github.com> Co-authored-by: Saikrishna Arcot Co-authored-by: bingwang-ms <66248323+bingwang-ms@users.noreply.github.com> --- azure-pipelines.yml | 39 + cfgmgr/buffer_pool_mellanox.lua | 19 +- cfgmgr/buffermgr.cpp | 35 +- cfgmgr/coppmgr.cpp | 10 +- cfgmgr/coppmgr.h | 3 +- cfgmgr/fabricmgr.cpp | 4 +- cfgmgr/fabricmgr.h | 2 +- configure.ac | 1 + fdbsyncd/fdbsync.cpp | 8 +- fpmsyncd/routesync.cpp | 8 +- fpmsyncd/routesync.h | 2 +- orchagent/Makefile.am | 3 +- orchagent/aclorch.cpp | 6 + orchagent/bufferorch.cpp | 6 +- orchagent/bulker.h | 43 + orchagent/crmorch.cpp | 19 +- orchagent/crmorch.h | 3 +- orchagent/dash/dashorch.cpp | 6 +- orchagent/fabricportsorch.cpp | 696 ++++++++++- orchagent/fabricportsorch.h | 16 + orchagent/intfsorch.cpp | 15 + orchagent/main.cpp | 4 - orchagent/neighorch.cpp | 58 +- orchagent/notifications.cpp | 6 + orchagent/notifications.h | 1 + orchagent/orchdaemon.cpp | 14 +- orchagent/orchdaemon.h | 1 + orchagent/p4orch/acl_rule_manager.cpp | 5 +- orchagent/p4orch/acl_rule_manager.h | 3 +- orchagent/p4orch/acl_table_manager.cpp | 5 +- orchagent/p4orch/acl_table_manager.h | 3 +- orchagent/p4orch/acl_util.cpp | 3 +- orchagent/p4orch/acl_util.h | 6 +- orchagent/p4orch/ext_tables_manager.cpp | 383 +++--- orchagent/p4orch/ext_tables_manager.h | 22 +- orchagent/p4orch/gre_tunnel_manager.cpp | 5 +- orchagent/p4orch/gre_tunnel_manager.h | 3 +- orchagent/p4orch/l3_admit_manager.cpp | 5 +- orchagent/p4orch/l3_admit_manager.h | 3 +- orchagent/p4orch/mirror_session_manager.cpp | 9 +- orchagent/p4orch/mirror_session_manager.h | 3 +- orchagent/p4orch/neighbor_manager.cpp | 12 +- orchagent/p4orch/neighbor_manager.h | 3 +- orchagent/p4orch/next_hop_manager.cpp | 9 +- orchagent/p4orch/next_hop_manager.h | 3 +- orchagent/p4orch/object_manager_interface.h | 3 +- orchagent/p4orch/p4orch.cpp | 8 +- orchagent/p4orch/p4orch.h | 24 +- orchagent/p4orch/p4orch_util.cpp | 6 +- orchagent/p4orch/p4orch_util.h | 51 +- orchagent/p4orch/route_manager.cpp | 5 +- orchagent/p4orch/route_manager.h | 3 +- orchagent/p4orch/router_interface_manager.cpp | 12 +- orchagent/p4orch/router_interface_manager.h | 3 +- .../p4orch/tables_definition_manager.cpp | 151 ++- orchagent/p4orch/tables_definition_manager.h | 13 +- orchagent/p4orch/tests/acl_manager_test.cpp | 4 +- .../p4orch/tests/fake_flexcounterorch.cpp | 10 +- orchagent/p4orch/tests/fake_portorch.cpp | 14 +- .../p4orch/tests/gre_tunnel_manager_test.cpp | 2 +- .../p4orch/tests/l3_admit_manager_test.cpp | 2 +- .../tests/mirror_session_manager_test.cpp | 2 +- .../p4orch/tests/neighbor_manager_test.cpp | 2 +- .../p4orch/tests/next_hop_manager_test.cpp | 2 +- orchagent/p4orch/tests/route_manager_test.cpp | 2 +- orchagent/p4orch/tests/test_main.cpp | 12 +- orchagent/p4orch/tests/wcmp_manager_test.cpp | 2 +- orchagent/p4orch/wcmp_manager.cpp | 9 +- orchagent/p4orch/wcmp_manager.h | 3 +- orchagent/port.h | 4 +- orchagent/port/porthlpr.cpp | 5 +- orchagent/port/porthlpr.h | 3 +- orchagent/port/portschema.h | 1 + orchagent/portsorch.cpp | 149 ++- orchagent/portsorch.h | 5 + orchagent/response_publisher.cpp | 9 +- orchagent/response_publisher.h | 6 +- orchagent/routeorch.cpp | 12 + orchagent/saihelper.cpp | 3 + orchagent/twamporch.cpp | 1053 +++++++++++++++++ orchagent/twamporch.h | 136 +++ tests/conftest.py | 9 + tests/dvslib/dvs_acl.py | 18 +- tests/dvslib/dvs_twamp.py | 98 ++ tests/mock_tests/Makefile.am | 8 +- tests/mock_tests/copp_ut.cpp | 26 +- tests/mock_tests/fake_producerstatetable.cpp | 7 +- tests/mock_tests/flowcounterrouteorch_ut.cpp | 1 + tests/mock_tests/fpmsyncd/test_routesync.cpp | 70 +- tests/mock_tests/mock_orch_test.h | 310 +++++ tests/mock_tests/mock_orchagent_main.h | 2 + tests/mock_tests/mock_sai_api.cpp | 25 + tests/mock_tests/mock_sai_api.h | 70 +- .../mock_tests/mock_subscriberstatetable.cpp | 30 + tests/mock_tests/mux_rollback_ut.cpp | 349 +----- tests/mock_tests/neighorch_ut.cpp | 198 ++++ tests/mock_tests/portal.h | 14 + tests/mock_tests/portsorch_ut.cpp | 152 ++- tests/mock_tests/routeorch_ut.cpp | 33 + tests/mock_tests/twamporch_ut.cpp | 975 +++++++++++++++ tests/mock_tests/ut_saihelper.cpp | 2 + tests/test_acl.py | 33 + tests/test_buffer_dynamic.py | 38 + tests/test_buffer_traditional.py | 115 +- tests/test_dash_vnet.py | 11 + tests/test_fabric.py | 8 + tests/test_fabric_port_isolation.py | 65 + tests/test_twamp.py | 182 +++ tests/test_virtual_chassis.py | 14 +- tests/test_vnet.py | 3 + tests/test_warm_reboot.py | 2 +- tests/virtual_chassis/8/default_config.json | 106 ++ 112 files changed, 5291 insertions(+), 927 deletions(-) create mode 100644 orchagent/twamporch.cpp create mode 100644 orchagent/twamporch.h create mode 100644 tests/dvslib/dvs_twamp.py create mode 100644 tests/mock_tests/mock_orch_test.h create mode 100644 tests/mock_tests/mock_sai_api.cpp create mode 100644 tests/mock_tests/mock_subscriberstatetable.cpp create mode 100644 tests/mock_tests/neighorch_ut.cpp create mode 100644 tests/mock_tests/twamporch_ut.cpp create mode 100644 tests/test_fabric_port_isolation.py create mode 100644 tests/test_twamp.py 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/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 9f71c19a7d..20d0bc09bc 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -369,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; } 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 6ef01a6ae0..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 @@ -518,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); @@ -732,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(); @@ -1072,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/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/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 8a92478add..9bf1aea3c2 100644 --- a/orchagent/p4orch/tests/test_main.cpp +++ b/orchagent/p4orch/tests/test_main.cpp @@ -103,7 +103,6 @@ bool parseHandleSaiStatusFailure(task_process_status status) return true; } - namespace { @@ -177,7 +176,7 @@ void AddVrf() } // namespace int main(int argc, char *argv[]) -{ +{ gBatchSize = DEFAULT_BATCH_SIZE; testing::InitGoogleTest(&argc, argv); @@ -245,6 +244,15 @@ int main(int argc, char *argv[]) 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 70fe7febbc..1a71639504 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -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/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/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/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 9c12401fa2..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, 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/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 8ca9a83d0d..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,241 +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); - - 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); - - ApplyDualTorConfigs(); INIT_SAI_API_MOCK(neighbor); INIT_SAI_API_MOCK(route); INIT_SAI_API_MOCK(acl); @@ -374,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(); } }; @@ -394,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 b9d918dc09..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); @@ -505,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); @@ -864,7 +936,6 @@ namespace portsorch_test std::deque kfvSerdes = {{ "Ethernet0", SET_COMMAND, { - { "admin_status", "up" }, { "idriver" , "0x6,0x6,0x6,0x6" } } }}; @@ -891,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); @@ -1171,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; @@ -1220,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(); } @@ -1383,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 @@ -1416,6 +1545,7 @@ namespace portsorch_test for (const auto &it : ports) { portTable.set(it.first, it.second); + transceieverInfoTable.set(it.first, {}); } // Set PortConfigDone, PortInitDone @@ -1463,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) @@ -1961,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/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index bd1f5bed2e..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,6 +198,10 @@ 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); @@ -511,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_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" + } } }