From 5384ace1440cb97cd4829e416e26b06f0361c9f8 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Wed, 3 Feb 2021 17:48:33 -0800 Subject: [PATCH 01/54] Reduce noise during frequent route update (#1624) Reduce loglevel to DEBUG for eth0 route update. Ref PR: #1606 --- fpmsyncd/routesync.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 30f6151e5e..7fae01eb3d 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -710,7 +710,7 @@ void RouteSync::onRouteMsg(int nlmsg_type, struct nl_object *obj, char *vrf) */ if (alias == "eth0" || alias == "docker0") { - SWSS_LOG_NOTICE("Skip routes to eth0 or docker0: %s %s %s", + SWSS_LOG_DEBUG("Skip routes to eth0 or docker0: %s %s %s", destipprefix, nexthops.c_str(), ifnames.c_str()); return; } From c7f27c85993b60945ed042ea4e59a3216c874947 Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Thu, 4 Feb 2021 12:50:44 -0800 Subject: [PATCH 02/54] [cfgmgr]: Integrate MACsecMGR with sonic-buildimage (#1627) 1. Fix bug about the return value of `get_value`, it should return `true` if it's OK 2. Fix bug about the type of `MACsecProfile::priority`, the original `uint8_t` will cause a `lexical_convert` failure when the priority is a integer. 3. Polish log in `get_value`. 4. Change the predefined paths for sonic-buildimage integration Signed-off-by: Ze Gan --- cfgmgr/macsecmgr.cpp | 23 +++++++++++++++-------- cfgmgr/macsecmgr.h | 2 +- cfgmgr/macsecmgrd.cpp | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cfgmgr/macsecmgr.cpp b/cfgmgr/macsecmgr.cpp index bf5344c8d0..57fdcd4c9a 100644 --- a/cfgmgr/macsecmgr.cpp +++ b/cfgmgr/macsecmgr.cpp @@ -24,11 +24,10 @@ using namespace std; using namespace swss; -#define WPA_SUPPLICANT_CMD "./wpa_supplicant" -#define WPA_CLI_CMD "./wpa_cli" -#define WPA_CONF "./wpa.conf" -// #define SOCK_DIR "/var/run/macsec/" -#define SOCK_DIR "./" +#define WPA_SUPPLICANT_CMD "/sbin/wpa_supplicant" +#define WPA_CLI_CMD "/sbin/wpa_cli" +#define WPA_CONF "/etc/wpa_supplicant.conf" +#define SOCK_DIR "/var/run/" constexpr std::uint64_t RETRY_TIME = 30; @@ -64,13 +63,21 @@ static bool get_value( auto value_opt = swss::fvsGetValue(ta, field, true); if (!value_opt) { - SWSS_LOG_WARN("Cannot find field : %s", field.c_str()); + SWSS_LOG_DEBUG("Cannot find field : %s", field.c_str()); return false; } - lexical_convert(*value_opt, value); + try + { + lexical_convert(*value_opt, value); + } + catch(const boost::bad_lexical_cast &e) + { + SWSS_LOG_ERROR("Cannot convert value(%s) in field(%s)", value_opt->c_str(), field.c_str()); + return false; + } - return false; + return true; } static void wpa_cli_commands(std::ostringstream & ostream) diff --git a/cfgmgr/macsecmgr.h b/cfgmgr/macsecmgr.h index 50bd04bbcf..eaefbd3e8b 100644 --- a/cfgmgr/macsecmgr.h +++ b/cfgmgr/macsecmgr.h @@ -27,7 +27,7 @@ class MACsecMgr : public Orch using TaskArgs = std::vector; struct MACsecProfile { - std::uint8_t priority; + std::uint32_t priority; std::string cipher_suite; std::string primary_cak; std::string primary_ckn; diff --git a/cfgmgr/macsecmgrd.cpp b/cfgmgr/macsecmgrd.cpp index 2cad8fc56e..f77e3d8c07 100644 --- a/cfgmgr/macsecmgrd.cpp +++ b/cfgmgr/macsecmgrd.cpp @@ -47,7 +47,7 @@ int main(int argc, char **argv) try { - // Logger::linkToDbNative("macsecmgrd"); + Logger::linkToDbNative("macsecmgrd"); SWSS_LOG_NOTICE("--- Starting macsecmgrd ---"); swss::DBConnector cfgDb("CONFIG_DB", 0); From 0b0d24cfec42c0921687eb14cdb16f37adbfe34f Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Thu, 4 Feb 2021 14:26:51 -0800 Subject: [PATCH 03/54] [Mux] Route handling based on mux status, kernel tunnel support (#1615) Program/Reprogram routes to hardware based on Mux status Create tunnel interface (tun0) in kernel Add/remove tunnel routes in kernel --- cfgmgr/tunnelmgr.cpp | 295 +++++++++++++++++++++++++++++++++--- cfgmgr/tunnelmgr.h | 20 ++- cfgmgr/tunnelmgrd.cpp | 6 +- orchagent/muxorch.cpp | 321 +++++++++++++++++++++++++++++++++------- orchagent/muxorch.h | 35 +++-- orchagent/neighorch.cpp | 53 ++++++- orchagent/neighorch.h | 5 +- orchagent/routeorch.cpp | 56 ++++++- orchagent/routeorch.h | 6 +- 9 files changed, 691 insertions(+), 106 deletions(-) diff --git a/cfgmgr/tunnelmgr.cpp b/cfgmgr/tunnelmgr.cpp index 848b4dba6b..327165e732 100644 --- a/cfgmgr/tunnelmgr.cpp +++ b/cfgmgr/tunnelmgr.cpp @@ -6,49 +6,154 @@ #include "logger.h" #include "tunnelmgr.h" +#include "tokenize.h" +#include "shellcmd.h" +#include "exec.h" using namespace std; using namespace swss; -TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, std::string tableName) : - Orch(cfgDb, tableName), - m_appIpInIpTunnelTable(appDb, APP_TUNNEL_DECAP_TABLE_NAME) +#define IPINIP "IPINIP" +#define TUNIF "tun0" +#define LOOPBACK_SRC "Loopback3" + +static int cmdIpTunnelIfCreate(const swss::TunnelInfo & info, std::string & res) +{ + // ip tunnel add {{tunnel intf}} mode ipip local {{dst ip}} remote {{remote ip}} + ostringstream cmd; + cmd << IP_CMD " tunnel add " + << TUNIF << " mode ipip local " + << shellquote(info.dst_ip) + << " remote " + << shellquote(info.remote_ip); + return swss::exec(cmd.str(), res); +} + +static int cmdIpTunnelIfRemove(std::string & res) { + // ip tunnel del {{tunnel intf}} + ostringstream cmd; + cmd << IP_CMD " tunnel del " + << TUNIF; + return swss::exec(cmd.str(), res); +} + +static int cmdIpTunnelIfUp(std::string & res) +{ + // ip link set dev {{tunnel intf}} up + ostringstream cmd; + cmd << IP_CMD " link set dev " + << TUNIF + << " up"; + return swss::exec(cmd.str(), res); +} + +static int cmdIpTunnelIfAddress(const std::string& ip, std::string & res) +{ + // ip addr add {{loopback3 ip}} dev {{tunnel intf}} + ostringstream cmd; + cmd << IP_CMD " addr add " + << shellquote(ip) + << " dev " + << TUNIF; + return swss::exec(cmd.str(), res); +} + +static int cmdIpTunnelRouteAdd(const std::string& pfx, std::string & res) +{ + // ip route add/replace {{ip prefix}} dev {{tunnel intf}} + // Replace route if route already exists + ostringstream cmd; + cmd << IP_CMD " route replace " + << shellquote(pfx) + << " dev " + << TUNIF; + return swss::exec(cmd.str(), res); +} + +static int cmdIpTunnelRouteDel(const std::string& pfx, std::string & res) +{ + // ip route del {{ip prefix}} dev {{tunnel intf}} + ostringstream cmd; + cmd << IP_CMD " route del " + << shellquote(pfx) + << " dev " + << TUNIF; + return swss::exec(cmd.str(), res); +} + +TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector &tableNames) : + Orch(cfgDb, tableNames), + m_appIpInIpTunnelTable(appDb, APP_TUNNEL_DECAP_TABLE_NAME), + m_cfgPeerTable(cfgDb, CFG_PEER_SWITCH_TABLE_NAME) +{ + std::vector peer_keys; + m_cfgPeerTable.getKeys(peer_keys); + + for (auto i: peer_keys) + { + std::vector fvs; + m_cfgPeerTable.get(i, fvs); + + for (auto j: fvs) + { + if (fvField(j) == "address_ipv4") + { + m_peerIp = fvValue(j); + break; + } + } + } + + auto consumerStateTable = new swss::ConsumerStateTable(appDb, APP_TUNNEL_ROUTE_TABLE_NAME, + TableConsumable::DEFAULT_POP_BATCH_SIZE, default_orch_pri); + auto consumer = new Consumer(consumerStateTable, this, APP_TUNNEL_ROUTE_TABLE_NAME); + Orch::addExecutor(consumer); + + // Cleanup any existing tunnel intf + std::string res; + cmdIpTunnelIfRemove(res); } void TunnelMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); + string table_name = consumer.getTableName(); + auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { - bool task_result = false; + bool task_result = true; KeyOpFieldsValuesTuple t = it->second; - const vector& data = kfvFieldsValues(t); - const std::string & op = kfvOp(t); if (op == SET_COMMAND) { - for (auto idx : data) + if (table_name == CFG_LOOPBACK_INTERFACE_TABLE_NAME) + { + task_result = doLpbkIntfTask(t); + } + else if (table_name == CFG_TUNNEL_TABLE_NAME) { - const auto &field = fvField(idx); - const auto &value = fvValue(idx); - if (field == "tunnel_type") - { - if (value == "IPINIP") - { - task_result = doIpInIpTunnelTask(t); - } - } + task_result = doTunnelTask(t); + } + else if (table_name == APP_TUNNEL_ROUTE_TABLE_NAME) + { + task_result = doTunnelRouteTask(t); } } else if (op == DEL_COMMAND) { - /* TODO: Handle Tunnel delete for other tunnel types */ - task_result = doIpInIpTunnelTask(t); + if (table_name == CFG_TUNNEL_TABLE_NAME) + { + task_result = doTunnelTask(t); + } + else if (table_name == APP_TUNNEL_ROUTE_TABLE_NAME) + { + task_result = doTunnelRouteTask(t); + } } else { @@ -66,22 +171,166 @@ void TunnelMgr::doTask(Consumer &consumer) } } -bool TunnelMgr::doIpInIpTunnelTask(const KeyOpFieldsValuesTuple & t) +bool TunnelMgr::doTunnelTask(const KeyOpFieldsValuesTuple & t) { SWSS_LOG_ENTER(); - const std::string & TunnelName = kfvKey(t); + const std::string & tunnelName = kfvKey(t); const std::string & op = kfvOp(t); + TunnelInfo tunInfo; + + for (auto fieldValue : kfvFieldsValues(t)) + { + const std::string & field = fvField(fieldValue); + const std::string & value = fvValue(fieldValue); + if (field == "dst_ip") + { + tunInfo.dst_ip = value; + } + else if (field == "tunnel_type") + { + tunInfo.type = value; + } + } if (op == SET_COMMAND) { - m_appIpInIpTunnelTable.set(TunnelName, kfvFieldsValues(t)); + if (tunInfo.type == IPINIP) + { + tunInfo.remote_ip = m_peerIp; + + if (!m_peerIp.empty() && !configIpTunnel(tunInfo)) + { + return false; + } + else if (m_peerIp.empty()) + { + SWSS_LOG_NOTICE("Peer/Remote IP not configured"); + } + + m_appIpInIpTunnelTable.set(tunnelName, kfvFieldsValues(t)); + } + m_tunnelCache[tunnelName] = tunInfo; } else { - m_appIpInIpTunnelTable.del(TunnelName); + auto it = m_tunnelCache.find(tunnelName); + + if (it == m_tunnelCache.end()) + { + SWSS_LOG_ERROR("Tunnel %s not found", tunnelName.c_str()); + return true; + } + + tunInfo = it->second; + if (tunInfo.type == IPINIP) + { + m_appIpInIpTunnelTable.del(tunnelName); + } + else + { + SWSS_LOG_WARN("Tunnel %s type %s is not handled", tunnelName.c_str(), tunInfo.type.c_str()); + } + m_tunnelCache.erase(tunnelName); } - SWSS_LOG_NOTICE("Tunnel %s task, op %s", TunnelName.c_str(), op.c_str()); + SWSS_LOG_NOTICE("Tunnel %s task, op %s", tunnelName.c_str(), op.c_str()); + return true; +} + +bool TunnelMgr::doLpbkIntfTask(const KeyOpFieldsValuesTuple & t) +{ + SWSS_LOG_ENTER(); + + vector keys = tokenize(kfvKey(t), config_db_key_delimiter); + + /* Skip entry with just interface name. Need to handle only IP prefix*/ + if (keys.size() == 1) + { + return true; + } + + string alias(keys[0]); + IpPrefix ipPrefix(keys[1]); + + m_intfCache[alias] = ipPrefix; + + if (alias == LOOPBACK_SRC && !m_tunnelCache.empty()) + { + int ret = 0; + std::string res; + ret = cmdIpTunnelIfAddress(ipPrefix.to_string(), res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to assign IP addr for tun if %s, res %s", + ipPrefix.to_string().c_str(), res.c_str()); + } + } + + SWSS_LOG_NOTICE("Loopback intf %s saved %s", alias.c_str(), ipPrefix.to_string().c_str()); + return true; +} + +bool TunnelMgr::doTunnelRouteTask(const KeyOpFieldsValuesTuple & t) +{ + SWSS_LOG_ENTER(); + + const std::string & prefix = kfvKey(t);; + const std::string & op = kfvOp(t); + + int ret = 0; + std::string res; + if (op == SET_COMMAND) + { + ret = cmdIpTunnelRouteAdd(prefix, res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to add route %s, res %s", prefix.c_str(), res.c_str()); + } + } + else + { + ret = cmdIpTunnelRouteDel(prefix, res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to del route %s, res %s", prefix.c_str(), res.c_str()); + } + } + + SWSS_LOG_INFO("Route updated to kernel %s, op %s", prefix.c_str(), op.c_str()); + return true; +} + + +bool TunnelMgr::configIpTunnel(const TunnelInfo& tunInfo) +{ + int ret = 0; + std::string res; + + ret = cmdIpTunnelIfCreate(tunInfo, res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to create IP tunnel if (dst ip: %s, peer ip %s), res %s", + tunInfo.dst_ip.c_str(),tunInfo.remote_ip.c_str(), res.c_str()); + } + + ret = cmdIpTunnelIfUp(res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to enable IP tunnel intf (dst ip: %s, peer ip %s), res %s", + tunInfo.dst_ip.c_str(),tunInfo.remote_ip.c_str(), res.c_str()); + } + + auto it = m_intfCache.find(LOOPBACK_SRC); + if (it != m_intfCache.end()) + { + ret = cmdIpTunnelIfAddress(it->second.to_string(), res); + if (ret != 0) + { + SWSS_LOG_WARN("Failed to assign IP addr for tun if %s, res %s", + it->second.to_string().c_str(), res.c_str()); + } + } + return true; } diff --git a/cfgmgr/tunnelmgr.h b/cfgmgr/tunnelmgr.h index 7c84e3e476..e2b601abe9 100644 --- a/cfgmgr/tunnelmgr.h +++ b/cfgmgr/tunnelmgr.h @@ -6,18 +6,34 @@ namespace swss { +struct TunnelInfo +{ + std::string type; + std::string dst_ip; + std::string remote_ip; +}; + class TunnelMgr : public Orch { public: - TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, std::string tableName); + TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector &tableNames); using Orch::doTask; private: void doTask(Consumer &consumer); - bool doIpInIpTunnelTask(const KeyOpFieldsValuesTuple & t); + bool doTunnelTask(const KeyOpFieldsValuesTuple & t); + bool doTunnelRouteTask(const KeyOpFieldsValuesTuple & t); + bool doLpbkIntfTask(const KeyOpFieldsValuesTuple & t); + + bool configIpTunnel(const TunnelInfo& info); ProducerStateTable m_appIpInIpTunnelTable; + Table m_cfgPeerTable; + + std::map m_tunnelCache; + std::map m_intfCache; + std::string m_peerIp; }; } diff --git a/cfgmgr/tunnelmgrd.cpp b/cfgmgr/tunnelmgrd.cpp index ea9e087123..d419b2b886 100644 --- a/cfgmgr/tunnelmgrd.cpp +++ b/cfgmgr/tunnelmgrd.cpp @@ -42,11 +42,15 @@ int main(int argc, char **argv) try { + vector cfgTunTables = { + CFG_TUNNEL_TABLE_NAME, + CFG_LOOPBACK_INTERFACE_TABLE_NAME + }; DBConnector cfgDb("CONFIG_DB", 0); DBConnector appDb("APPL_DB", 0); - TunnelMgr tunnelmgr(&cfgDb, &appDb, CFG_TUNNEL_TABLE_NAME); + TunnelMgr tunnelmgr(&cfgDb, &appDb, cfgTunTables); std::vector cfgOrchList = {&tunnelmgr}; diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 18bab8b10c..124a1cda7c 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -18,11 +18,13 @@ #include "neighorch.h" #include "portsorch.h" #include "aclorch.h" +#include "routeorch.h" /* Global variables */ extern Directory gDirectory; extern CrmOrch *gCrmOrch; extern NeighOrch *gNeighOrch; +extern RouteOrch *gRouteOrch; extern AclOrch *gAclOrch; extern PortsOrch *gPortsOrch; @@ -316,7 +318,7 @@ bool MuxCable::stateInitActive() { SWSS_LOG_INFO("Set state to Active from %s", muxStateValToString.at(state_).c_str()); - if (!nbrHandler()) + if (!nbrHandler(true, false)) { return false; } @@ -341,23 +343,11 @@ bool MuxCable::stateActive() return false; } - if (!nbrHandler()) + if (!nbrHandler(true)) { return false; } - if (remove_route(srv_ip4_) != SAI_STATUS_SUCCESS) - { - return false; - } - - if (remove_route(srv_ip6_) != SAI_STATUS_SUCCESS) - { - return false; - } - - mux_orch_->removeNextHopTunnel(MUX_TUNNEL, peer_ip4_); - return true; } @@ -372,36 +362,13 @@ bool MuxCable::stateStandby() return false; } - sai_object_id_t nh = mux_orch_->createNextHopTunnel(MUX_TUNNEL, peer_ip4_); - - if (nh == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_INFO("Null NH object id, retry for %s", peer_ip4_.to_string().c_str()); - return false; - } - - if (create_route(srv_ip4_, nh) != SAI_STATUS_SUCCESS) - { - return false; - } - - if (create_route(srv_ip6_, nh) != SAI_STATUS_SUCCESS) - { - remove_route(srv_ip4_); - return false; - } - if (!nbrHandler(false)) { - remove_route(srv_ip4_); - remove_route(srv_ip6_); return false; } if (!aclHandler(port.m_port_id)) { - remove_route(srv_ip4_); - remove_route(srv_ip6_); SWSS_LOG_INFO("Add ACL drop rule failed for %s", mux_name_.c_str()); return false; } @@ -480,70 +447,232 @@ bool MuxCable::isIpInSubnet(IpAddress ip) } } -bool MuxCable::nbrHandler(bool enable) +bool MuxCable::nbrHandler(bool enable, bool update_rt) { if (enable) { - return nbr_handler_->enable(); + return nbr_handler_->enable(update_rt); + } + else + { + sai_object_id_t tnh = mux_orch_->createNextHopTunnel(MUX_TUNNEL, peer_ip4_); + if (tnh == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Null NH object id, retry for %s", peer_ip4_.to_string().c_str()); + return false; + } + + return nbr_handler_->disable(tnh); + } +} + +void MuxCable::updateNeighbor(NextHopKey nh, bool add) +{ + sai_object_id_t tnh = mux_orch_->getNextHopTunnelId(MUX_TUNNEL, peer_ip4_); + nbr_handler_->update(nh, tnh, add, state_); + if (add) + { + mux_orch_->addNexthop(nh, mux_name_); } else { - return nbr_handler_->disable(); + mux_orch_->removeNexthop(nh); } } -void MuxNbrHandler::update(IpAddress ip, string alias, bool add) +void MuxNbrHandler::update(NextHopKey nh, sai_object_id_t tunnelId, bool add, MuxState state) { + SWSS_LOG_INFO("Neigh %s on %s, add %d, state %d", + nh.ip_address.to_string().c_str(), nh.alias.c_str(), add, state); + + MuxCableOrch* mux_cb_orch = gDirectory.get(); + IpPrefix pfx = nh.ip_address.to_string(); + if (add) { - neighbors_.add(ip); - if (!alias.empty() && alias != alias_) + if (!nh.alias.empty() && nh.alias != alias_) { - alias_ = alias; + alias_ = nh.alias; + } + + if (neighbors_.find(nh.ip_address) != neighbors_.end()) + { + return; + } + + switch (state) + { + case MuxState::MUX_STATE_INIT: + neighbors_[nh.ip_address] = SAI_NULL_OBJECT_ID; + break; + case MuxState::MUX_STATE_ACTIVE: + neighbors_[nh.ip_address] = gNeighOrch->getLocalNextHopId(nh); + break; + case MuxState::MUX_STATE_STANDBY: + neighbors_[nh.ip_address] = tunnelId; + mux_cb_orch->addTunnelRoute(nh); + create_route(pfx, tunnelId); + break; + default: + SWSS_LOG_NOTICE("State '%s' not handled for nbr %s update", + muxStateValToString.at(state).c_str(), nh.ip_address.to_string().c_str()); + break; } } else { - neighbors_.remove(ip); + /* if current state is standby, remove the tunnel route */ + if (state == MuxState::MUX_STATE_STANDBY) + { + remove_route(pfx); + mux_cb_orch->removeTunnelRoute(nh); + } + neighbors_.erase(nh.ip_address); } } -bool MuxNbrHandler::enable() +bool MuxNbrHandler::enable(bool update_rt) { NeighborEntry neigh; + MuxCableOrch* mux_cb_orch = gDirectory.get(); - auto it = neighbors_.getIpAddresses().begin(); - while (it != neighbors_.getIpAddresses().end()) + auto it = neighbors_.begin(); + while (it != neighbors_.end()) { - neigh = NeighborEntry(*it, alias_); + SWSS_LOG_INFO("Enabling neigh %s on %s", it->first.to_string().c_str(), alias_.c_str()); + + neigh = NeighborEntry(it->first, alias_); if (!gNeighOrch->enableNeighbor(neigh)) { + SWSS_LOG_INFO("Enabling neigh failed for %s", neigh.ip_address.to_string().c_str()); + return false; + } + + /* Update NH to point to learned neighbor */ + it->second = gNeighOrch->getLocalNextHopId(neigh); + + /* Reprogram route */ + NextHopKey nh_key = NextHopKey(it->first, alias_); + uint32_t num_routes = 0; + if (!gRouteOrch->updateNextHopRoutes(nh_key, num_routes)) + { + SWSS_LOG_INFO("Update route failed for NH %s", nh_key.ip_address.to_string().c_str()); + return false; + } + + /* Increment ref count for new NHs */ + gNeighOrch->increaseNextHopRefCount(nh_key, num_routes); + + /* + * Invalidate current nexthop group and update with new NH + * Ref count update is not required for tunnel NH IDs (nh_removed) + */ + uint32_t nh_removed, nh_added; + if (!gRouteOrch->invalidnexthopinNextHopGroup(nh_key, nh_removed)) + { + SWSS_LOG_ERROR("Removing existing NH failed for %s", nh_key.ip_address.to_string().c_str()); + return false; + } + + if (!gRouteOrch->validnexthopinNextHopGroup(nh_key, nh_added)) + { + SWSS_LOG_ERROR("Adding NH failed for %s", nh_key.ip_address.to_string().c_str()); return false; } + + /* Increment ref count for ECMP NH members */ + gNeighOrch->increaseNextHopRefCount(nh_key, nh_added); + + IpPrefix pfx = it->first.to_string(); + if (update_rt) + { + if (remove_route(pfx) != SAI_STATUS_SUCCESS) + { + return false; + } + mux_cb_orch->removeTunnelRoute(nh_key); + } + it++; } return true; } -bool MuxNbrHandler::disable() +bool MuxNbrHandler::disable(sai_object_id_t tnh) { NeighborEntry neigh; + MuxCableOrch* mux_cb_orch = gDirectory.get(); - auto it = neighbors_.getIpAddresses().begin(); - while (it != neighbors_.getIpAddresses().end()) + auto it = neighbors_.begin(); + while (it != neighbors_.end()) { - neigh = NeighborEntry(*it, alias_); + SWSS_LOG_INFO("Disabling neigh %s on %s", it->first.to_string().c_str(), alias_.c_str()); + + /* Update NH to point to Tunnel nexhtop */ + it->second = tnh; + + /* Reprogram route */ + NextHopKey nh_key = NextHopKey(it->first, alias_); + uint32_t num_routes = 0; + if (!gRouteOrch->updateNextHopRoutes(nh_key, num_routes)) + { + SWSS_LOG_INFO("Update route failed for NH %s", nh_key.ip_address.to_string().c_str()); + return false; + } + + /* Decrement ref count for old NHs */ + gNeighOrch->decreaseNextHopRefCount(nh_key, num_routes); + + /* Invalidate current nexthop group and update with new NH */ + uint32_t nh_removed, nh_added; + if (!gRouteOrch->invalidnexthopinNextHopGroup(nh_key, nh_removed)) + { + SWSS_LOG_ERROR("Removing existing NH failed for %s", nh_key.ip_address.to_string().c_str()); + return false; + } + + /* Decrement ref count for ECMP NH members */ + gNeighOrch->decreaseNextHopRefCount(nh_key, nh_removed); + + if (!gRouteOrch->validnexthopinNextHopGroup(nh_key, nh_added)) + { + SWSS_LOG_ERROR("Adding NH failed for %s", nh_key.ip_address.to_string().c_str()); + return false; + } + + neigh = NeighborEntry(it->first, alias_); if (!gNeighOrch->disableNeighbor(neigh)) + { + SWSS_LOG_INFO("Disabling neigh failed for %s", neigh.ip_address.to_string().c_str()); + return false; + } + + mux_cb_orch->addTunnelRoute(nh_key); + + IpPrefix pfx = it->first.to_string(); + if (create_route(pfx, it->second) != SAI_STATUS_SUCCESS) { return false; } + it++; } return true; } +sai_object_id_t MuxNbrHandler::getNextHopId(const NextHopKey nhKey) +{ + auto it = neighbors_.find(nhKey.ip_address); + if (it != neighbors_.end()) + { + return it->second; + } + + return SAI_NULL_OBJECT_ID; +} + std::map MuxAclHandler::acl_table_; MuxAclHandler::MuxAclHandler(sai_object_id_t port) @@ -626,7 +755,6 @@ sai_object_id_t MuxOrch::createNextHopTunnel(std::string tunnelKey, swss::IpAddr auto it = mux_tunnel_nh_.find(ipAddr); if (it != mux_tunnel_nh_.end()) { - ++it->second.ref_count; return it->second.nh_id; } @@ -666,6 +794,19 @@ bool MuxOrch::removeNextHopTunnel(std::string tunnelKey, swss::IpAddress& ipAddr return true; } +sai_object_id_t MuxOrch::getNextHopTunnelId(std::string tunnelKey, IpAddress& ipAddr) +{ + auto it = mux_tunnel_nh_.find(ipAddr); + if (it == mux_tunnel_nh_.end()) + { + SWSS_LOG_INFO("NH doesn't exist %s, ip %s", tunnelKey.c_str(), ipAddr.to_string().c_str()); + return SAI_NULL_OBJECT_ID; + } + + return it->second.nh_id; +} + + MuxCable* MuxOrch::findMuxCableInSubnet(IpAddress ip) { for (auto it = mux_cable_tb_.begin(); it != mux_cable_tb_.end(); it++) @@ -699,11 +840,40 @@ void MuxOrch::updateNeighbor(const NeighborUpdate& update) MuxCable* ptr = it->second.get(); if (ptr->isIpInSubnet(update.entry.ip_address)) { - ptr->updateNeighbor(update.entry.ip_address, update.entry.alias, update.add); + ptr->updateNeighbor(update.entry, update.add); } } } +void MuxOrch::addNexthop(NextHopKey nh, string muxName) +{ + mux_nexthop_tb_[nh] = muxName; +} + +void MuxOrch::removeNexthop(NextHopKey nh) +{ + mux_nexthop_tb_.erase(nh); +} + +sai_object_id_t MuxOrch::getNextHopId(const NextHopKey &nh) +{ + if (mux_nexthop_tb_.find(nh) == mux_nexthop_tb_.end()) + { + return SAI_NULL_OBJECT_ID; + } + + auto mux_name = mux_nexthop_tb_[nh]; + if (!isMuxExists(mux_name)) + { + SWSS_LOG_WARN("Mux entry for port '%s' doesn't exist", mux_name.c_str()); + return SAI_NULL_OBJECT_ID; + } + + auto ptr = getMuxCable(mux_name); + + return ptr->getNextHopId(nh); +} + void MuxOrch::update(SubjectType type, void *cntx) { SWSS_LOG_ENTER(); @@ -868,7 +1038,8 @@ bool MuxOrch::delOperation(const Request& request) } MuxCableOrch::MuxCableOrch(DBConnector *db, const std::string& tableName): - Orch2(db, tableName, request_) + Orch2(db, tableName, request_), + app_tunnel_route_table_(db, APP_TUNNEL_ROUTE_TABLE_NAME) { mux_table_ = unique_ptr(new Table(db, APP_HW_MUX_CABLE_TABLE_NAME)); } @@ -881,6 +1052,44 @@ void MuxCableOrch::updateMuxState(string portName, string muxState) mux_table_->set(portName, tuples); } +void MuxCableOrch::addTunnelRoute(const NextHopKey &nhKey) +{ + if (!nhKey.ip_address.isV4()) + { + SWSS_LOG_INFO("IPv6 tunnel route add '%s' - (Not Implemented)", nhKey.ip_address.to_string().c_str()); + return; + } + + vector data; + string key, alias = nhKey.alias; + + IpPrefix pfx = nhKey.ip_address.to_string(); + key = pfx.to_string(); + + FieldValueTuple fvTuple("alias", alias); + data.push_back(fvTuple); + + SWSS_LOG_INFO("Add tunnel route DB '%s:%s'", alias.c_str(), key.c_str()); + app_tunnel_route_table_.set(key, data); +} + +void MuxCableOrch::removeTunnelRoute(const NextHopKey &nhKey) +{ + if (!nhKey.ip_address.isV4()) + { + SWSS_LOG_INFO("IPv6 tunnel route remove '%s' - (Not Implemented)", nhKey.ip_address.to_string().c_str()); + return; + } + + string key, alias = nhKey.alias; + + IpPrefix pfx = nhKey.ip_address.to_string(); + key = pfx.to_string(); + + SWSS_LOG_INFO("Remove tunnel route DB '%s:%s'", alias.c_str(), key.c_str()); + app_tunnel_route_table_.del(key); +} + bool MuxCableOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index 2d3c15c1cd..2e97c5b609 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -9,6 +9,7 @@ #include "portsorch.h" #include "tunneldecaporch.h" #include "aclorch.h" +#include "neighorch.h" enum MuxState { @@ -49,18 +50,23 @@ class MuxAclHandler sai_object_id_t port_ = SAI_NULL_OBJECT_ID; }; -// Mux Neighbor Handler for adding/removing neigbhors +// IP to nexthop index mapping +typedef std::map MuxNeighbor; + +// Mux Neighbor Handler for adding/removing neighbors class MuxNbrHandler { public: MuxNbrHandler() = default; - bool enable(); - bool disable(); - void update(IpAddress, string alias = "", bool = true); + bool enable(bool update_rt); + bool disable(sai_object_id_t); + void update(NextHopKey nh, sai_object_id_t, bool = true, MuxState = MuxState::MUX_STATE_INIT); + + sai_object_id_t getNextHopId(const NextHopKey); private: - IpAddresses neighbors_; + MuxNeighbor neighbors_; string alias_; }; @@ -83,9 +89,10 @@ class MuxCable bool isStateChangeInProgress() { return st_chg_in_progress_; } bool isIpInSubnet(IpAddress ip); - void updateNeighbor(IpAddress ip, string alias, bool add) + void updateNeighbor(NextHopKey nh, bool add); + sai_object_id_t getNextHopId(const NextHopKey nh) { - nbr_handler_->update(ip, alias, add); + return nbr_handler_->getNextHopId(nh); } private: @@ -93,8 +100,8 @@ class MuxCable bool stateInitActive(); bool stateStandby(); - bool aclHandler(sai_object_id_t, bool = true); - bool nbrHandler(bool = true); + bool aclHandler(sai_object_id_t, bool add = true); + bool nbrHandler(bool enable, bool update_routes = true); string mux_name_; @@ -133,6 +140,7 @@ struct NHTunnel typedef std::unique_ptr MuxCable_T; typedef std::map MuxCableTb; typedef std::map MuxTunnelNHs; +typedef std::map NextHopTb; class MuxCfgRequest : public Request { @@ -164,8 +172,13 @@ class MuxOrch : public Orch2, public Observer, public Subject void update(SubjectType, void *); void updateNeighbor(const NeighborUpdate&); + void addNexthop(NextHopKey, string); + void removeNexthop(NextHopKey); + sai_object_id_t getNextHopId(const NextHopKey&); + sai_object_id_t createNextHopTunnel(std::string tunnelKey, IpAddress& ipAddr); bool removeNextHopTunnel(std::string tunnelKey, IpAddress& ipAddr); + sai_object_id_t getNextHopTunnelId(std::string tunnelKey, IpAddress& ipAddr); private: virtual bool addOperation(const Request& request); @@ -179,6 +192,7 @@ class MuxOrch : public Orch2, public Observer, public Subject MuxCableTb mux_cable_tb_; MuxTunnelNHs mux_tunnel_nh_; + NextHopTb mux_nexthop_tb_; handler_map handler_map_; @@ -208,6 +222,8 @@ class MuxCableOrch : public Orch2 MuxCableOrch(DBConnector *db, const std::string& tableName); void updateMuxState(string portName, string muxState); + void addTunnelRoute(const NextHopKey &nhKey); + void removeTunnelRoute(const NextHopKey &nhKey); private: virtual bool addOperation(const Request& request); @@ -215,6 +231,7 @@ class MuxCableOrch : public Orch2 unique_ptr
mux_table_; MuxCableRequest request_; + ProducerStateTable app_tunnel_route_table_; }; const request_description_t mux_state_request_description = { diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 246d044a8b..267e51544a 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -135,8 +135,17 @@ void NeighOrch::update(SubjectType type, void *cntx) return; } + bool NeighOrch::hasNextHop(const NextHopKey &nexthop) { + // First check if mux has NH + MuxOrch* mux_orch = gDirectory.get(); + sai_object_id_t nhid = mux_orch->getNextHopId(nexthop); + if (nhid != SAI_NULL_OBJECT_ID) + { + return true; + } + return m_syncdNextHops.find(nexthop) != m_syncdNextHops.end(); } @@ -250,11 +259,11 @@ bool NeighOrch::setNextHopFlag(const NextHopKey &nexthop, const uint32_t nh_flag } nhop->second.nh_flags |= nh_flag; - + uint32_t count; switch (nh_flag) { case NHFLAGS_IFDOWN: - rc = gRouteOrch->invalidnexthopinNextHopGroup(nexthop); + rc = gRouteOrch->invalidnexthopinNextHopGroup(nexthop, count); break; default: assert(0); @@ -279,11 +288,11 @@ bool NeighOrch::clearNextHopFlag(const NextHopKey &nexthop, const uint32_t nh_fl } nhop->second.nh_flags &= ~nh_flag; - + uint32_t count; switch (nh_flag) { case NHFLAGS_IFDOWN: - rc = gRouteOrch->validnexthopinNextHopGroup(nexthop); + rc = gRouteOrch->validnexthopinNextHopGroup(nexthop, count); break; default: assert(0); @@ -391,9 +400,31 @@ bool NeighOrch::removeOverlayNextHop(const NextHopKey &nexthop) return true; } +sai_object_id_t NeighOrch::getLocalNextHopId(const NextHopKey& nexthop) +{ + if (m_syncdNextHops.find(nexthop) == m_syncdNextHops.end()) + { + return SAI_NULL_OBJECT_ID; + } + + return m_syncdNextHops[nexthop].next_hop_id; +} + sai_object_id_t NeighOrch::getNextHopId(const NextHopKey &nexthop) { assert(hasNextHop(nexthop)); + + /* + * The nexthop id could be varying depending on the use-case + * For e.g, a route could have a direct neighbor but may require + * to be tx via tunnel nexthop + */ + MuxOrch* mux_orch = gDirectory.get(); + sai_object_id_t nhid = mux_orch->getNextHopId(nexthop); + if (nhid != SAI_NULL_OBJECT_ID) + { + return nhid; + } return m_syncdNextHops[nexthop].next_hop_id; } @@ -403,16 +434,22 @@ int NeighOrch::getNextHopRefCount(const NextHopKey &nexthop) return m_syncdNextHops[nexthop].ref_count; } -void NeighOrch::increaseNextHopRefCount(const NextHopKey &nexthop) +void NeighOrch::increaseNextHopRefCount(const NextHopKey &nexthop, uint32_t count) { assert(hasNextHop(nexthop)); - m_syncdNextHops[nexthop].ref_count ++; + if (m_syncdNextHops.find(nexthop) != m_syncdNextHops.end()) + { + m_syncdNextHops[nexthop].ref_count += count; + } } -void NeighOrch::decreaseNextHopRefCount(const NextHopKey &nexthop) +void NeighOrch::decreaseNextHopRefCount(const NextHopKey &nexthop, uint32_t count) { assert(hasNextHop(nexthop)); - m_syncdNextHops[nexthop].ref_count --; + if (m_syncdNextHops.find(nexthop) != m_syncdNextHops.end()) + { + m_syncdNextHops[nexthop].ref_count -= count; + } } bool NeighOrch::getNeighborEntry(const NextHopKey &nexthop, NeighborEntry &neighborEntry, MacAddress &macAddress) diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 20b6f3913c..60981e9f11 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -50,10 +50,11 @@ class NeighOrch : public Orch, public Subject, public Observer bool hasNextHop(const NextHopKey&); sai_object_id_t getNextHopId(const NextHopKey&); + sai_object_id_t getLocalNextHopId(const NextHopKey&); int getNextHopRefCount(const NextHopKey&); - void increaseNextHopRefCount(const NextHopKey&); - void decreaseNextHopRefCount(const NextHopKey&); + void increaseNextHopRefCount(const NextHopKey&, uint32_t count = 1); + void decreaseNextHopRefCount(const NextHopKey&, uint32_t count = 1); bool getNeighborEntry(const NextHopKey&, NeighborEntry&, MacAddress&); bool getNeighborEntry(const IpAddress&, NeighborEntry&, MacAddress&); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 4eb830f8fc..fdec0db3cf 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -295,12 +295,13 @@ void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr, sai_object_ } } -bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop) +bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t& count) { SWSS_LOG_ENTER(); sai_object_id_t nexthop_id; sai_status_t status; + count = 0; for (auto nhopgroup = m_syncdNextHopGroups.begin(); nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) @@ -333,6 +334,7 @@ bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop) return false; } + ++count; gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); nhopgroup->second.nhopgroup_members[nexthop] = nexthop_id; } @@ -345,12 +347,13 @@ bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop) return true; } -bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop) +bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t& count) { SWSS_LOG_ENTER(); sai_object_id_t nexthop_id; sai_status_t status; + count = 0; for (auto nhopgroup = m_syncdNextHopGroups.begin(); nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) @@ -371,6 +374,7 @@ bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop) return false; } + ++count; gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); } @@ -562,7 +566,7 @@ void RouteOrch::doTask(Consumer& consumer) * way is to create loopback interface and then create * route pointing to it, so that we can traps packets to * CPU */ - if (alias == "eth0" || alias == "docker0" || + if (alias == "eth0" || alias == "docker0" || alias == "tun0" || alias == "lo" || !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX)) { excp_intfs_flag = true; @@ -1190,6 +1194,52 @@ bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) return true; } +bool RouteOrch::updateNextHopRoutes(const NextHopKey& nextHop, uint32_t& numRoutes) +{ + numRoutes = 0; + sai_route_entry_t route_entry; + sai_attribute_t route_attr; + sai_object_id_t next_hop_id; + + for (auto rt_table : m_syncdRoutes) + { + for (auto rt_entry : rt_table.second) + { + // Skip routes with ecmp nexthops + if (rt_entry.second.getSize() > 1) + { + continue; + } + + if (rt_entry.second.contains(nextHop)) + { + SWSS_LOG_INFO("Updating route %s during nexthop status change", + rt_entry.first.to_string().c_str()); + next_hop_id = m_neighOrch->getNextHopId(nextHop); + + route_entry.vr_id = rt_table.first; + route_entry.switch_id = gSwitchId; + copy(route_entry.destination, rt_entry.first); + + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + route_attr.value.oid = next_hop_id; + + sai_status_t status = sai_route_api->set_route_entry_attribute(&route_entry, &route_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update route %s, rv:%d", + rt_entry.first.to_string().c_str(), status); + return false; + } + + ++numRoutes; + } + } + } + + return true; +} + void RouteOrch::addTempRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) { SWSS_LOG_ENTER(); diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index 214c2bd4ae..a2ca2bdc90 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -107,8 +107,10 @@ class RouteOrch : public Orch, public Subject bool addNextHopGroup(const NextHopGroupKey&); bool removeNextHopGroup(const NextHopGroupKey&); - bool validnexthopinNextHopGroup(const NextHopKey&); - bool invalidnexthopinNextHopGroup(const NextHopKey&); + bool updateNextHopRoutes(const NextHopKey&, uint32_t&); + + bool validnexthopinNextHopGroup(const NextHopKey&, uint32_t&); + bool invalidnexthopinNextHopGroup(const NextHopKey&, uint32_t&); bool createRemoteVtep(sai_object_id_t, const NextHopKey&); bool deleteRemoteVtep(sai_object_id_t, const NextHopKey&); From 1438a703a0f23989c6bb0e771cfce4fbb851b2f1 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Sat, 6 Feb 2021 16:35:16 +0800 Subject: [PATCH 04/54] Fix the compiling errors in gcc9 (#1621) --- orchagent/orch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index f1b0089ad7..e26439e7b8 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -606,7 +606,7 @@ ref_resolve_status Orch::resolveFieldRefArray( SWSS_LOG_DEBUG("Resolved to sai_object:0x%" PRIx64 ", type:%s, name:%s", sai_obj, ref_type_name.c_str(), object_name.c_str()); sai_object_arr.push_back(sai_obj); if (!object_name_list.empty()) - object_name_list += string(&list_item_delimiter); + object_name_list += list_item_delimiter; object_name_list += ref_type_name + delimiter + object_name; } count++; From 5fa2329b895836e9081f2db3aad4219935f79d77 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Mon, 8 Feb 2021 05:19:49 +0800 Subject: [PATCH 05/54] Support shared headroom pool on top of dynamic buffer calculation (#1581) * Support shared headroom pool on top of dynamic buffer calculation - Feature is enabled/disabled on-the-fly by configuring over-subscribe-ration and shared headroom pool size. If both are configured, the shared headroom pool size will take effect. When turn on/off the feature, all the lossless profiles and buffer pool size will be recalculated. - Support calculating shared headroom pool while ingress lossless pool is statically configured. - Check accumulative headroom before toggling SHP state To disable SHP results in size of PG increasing. Hence needs to check whether accumulative headroom exceed limit - Split the function doUpdateStaticProfileTask into two functions Originally it was called for static profile only and consisted of two parts: - One is for dynamic th updated. It will go over all the buffer profiles dynamically generated according to the dynamic th and update them - The other is for size updated. It will go over each port referencing the profile and check whether the accumulative headroom exceeds limit Now that it is also called by shared headroom pool, we split it into two functions to make it more clear Signed-off-by: Stephen Sun How I verified it Run vs test and regression test. --- cfgmgr/buffer_headroom_mellanox.lua | 22 +- cfgmgr/buffer_pool_mellanox.lua | 57 ++++- cfgmgr/buffermgrdyn.cpp | 309 +++++++++++++++++++++++++--- cfgmgr/buffermgrdyn.h | 22 +- tests/test_buffer_dynamic.py | 92 +++++++++ 5 files changed, 465 insertions(+), 37 deletions(-) diff --git a/cfgmgr/buffer_headroom_mellanox.lua b/cfgmgr/buffer_headroom_mellanox.lua index cbc6d2d6ec..2c95814008 100644 --- a/cfgmgr/buffer_headroom_mellanox.lua +++ b/cfgmgr/buffer_headroom_mellanox.lua @@ -16,6 +16,7 @@ local lossless_mtu local small_packet_percentage +local over_subscribe_ratio = 0 local cell_size local pipeline_latency local mac_phy_delay @@ -72,8 +73,19 @@ for i = 1, #lossless_traffic_table_content, 2 do end end --- Fetch DEFAULT_LOSSLESS_BUFFER_PARAMETER from CONFIG_DB -local lossless_traffic_keys = redis.call('KEYS', 'DEFAULT_LOSSLESS_BUFFER_PARAMETER*') +-- Fetch over subscribe ratio +local default_lossless_param_keys = redis.call('KEYS', 'DEFAULT_LOSSLESS_BUFFER_PARAMETER*') +local over_subscribe_ratio = tonumber(redis.call('HGET', default_lossless_param_keys[1], 'over_subscribe_ratio')) + +-- Fetch the shared headroom pool size +local shp_size = tonumber(redis.call('HGET', 'BUFFER_POOL|ingress_lossless_pool', 'xoff')) + +local shp_enabled +if shp_size ~= nil and shp_size ~= 0 or over_subscribe_ratio ~= nil and over_subscribe_ratio ~= 0 then + shp_enabled = true +else + shp_enabled = false +end -- Calculate the headroom information local speed_of_light = 198000000 @@ -119,7 +131,11 @@ xoff_value = math.ceil(xoff_value / 1024) * 1024 xon_value = pipeline_latency xon_value = math.ceil(xon_value / 1024) * 1024 -headroom_size = xoff_value + xon_value + speed_overhead +if shp_enabled then + headroom_size = xon_value +else + headroom_size = xoff_value + xon_value + speed_overhead +end headroom_size = math.ceil(headroom_size / 1024) * 1024 table.insert(ret, "xon" .. ":" .. math.ceil(xon_value)) diff --git a/cfgmgr/buffer_pool_mellanox.lua b/cfgmgr/buffer_pool_mellanox.lua index da88f186c0..9adbb15a6a 100644 --- a/cfgmgr/buffer_pool_mellanox.lua +++ b/cfgmgr/buffer_pool_mellanox.lua @@ -83,6 +83,24 @@ end local egress_lossless_pool_size = redis.call('HGET', 'BUFFER_POOL|egress_lossless_pool', 'size') +-- Whether shared headroom pool is enabled? +local default_lossless_param_keys = redis.call('KEYS', 'DEFAULT_LOSSLESS_BUFFER_PARAMETER*') +local over_subscribe_ratio = tonumber(redis.call('HGET', default_lossless_param_keys[1], 'over_subscribe_ratio')) + +-- Fetch the shared headroom pool size +local shp_size = tonumber(redis.call('HGET', 'BUFFER_POOL|ingress_lossless_pool', 'xoff')) + +local shp_enabled = false +if over_subscribe_ratio ~= nil and over_subscribe_ratio ~= 0 then + shp_enabled = true +end + +if shp_size ~= nil and shp_size ~= 0 then + shp_enabled = true +else + shp_size = 0 +end + -- Switch to APPL_DB redis.call('SELECT', appl_db) @@ -103,6 +121,7 @@ local statistics = {} -- Fetch sizes of all of the profiles, accumulate them local accumulative_occupied_buffer = 0 +local accumulative_xoff = 0 for i = 1, #profiles, 1 do if profiles[i][1] ~= "BUFFER_PROFILE_TABLE_KEY_SET" and profiles[i][1] ~= "BUFFER_PROFILE_TABLE_DEL_SET" then local size = tonumber(redis.call('HGET', profiles[i][1], 'size')) @@ -114,6 +133,13 @@ for i = 1, #profiles, 1 do profiles[i][2] = count_up_port end if size ~= 0 then + if shp_enabled and shp_size == 0 then + local xon = tonumber(redis.call('HGET', profiles[i][1], 'xon')) + local xoff = tonumber(redis.call('HGET', profiles[i][1], 'xoff')) + if xon ~= nil and xoff ~= nil and xon + xoff > size then + accumulative_xoff = accumulative_xoff + (xon + xoff - size) * profiles[i][2] + end + end accumulative_occupied_buffer = accumulative_occupied_buffer + size * profiles[i][2] end table.insert(statistics, {profiles[i][1], size, profiles[i][2]}) @@ -138,7 +164,7 @@ end local asic_keys = redis.call('KEYS', 'ASIC_TABLE*') local cell_size = tonumber(redis.call('HGET', asic_keys[1], 'cell_size')) --- Align mmu_size at cell size boundary, otherwith the sdk will complain and the syncd will faill +-- Align mmu_size at cell size boundary, otherwise the sdk will complain and the syncd will fail local number_of_cells = math.floor(mmu_size / cell_size) local ceiling_mmu_size = number_of_cells * cell_size @@ -149,11 +175,16 @@ redis.call('SELECT', config_db) local pools_need_update = {} local ipools = redis.call('KEYS', 'BUFFER_POOL|ingress*') local ingress_pool_count = 0 +local ingress_lossless_pool_size = nil for i = 1, #ipools, 1 do local size = tonumber(redis.call('HGET', ipools[i], 'size')) if not size then table.insert(pools_need_update, ipools[i]) ingress_pool_count = ingress_pool_count + 1 + else + if ipools[i] == 'BUFFER_POOL|ingress_lossless_pool' and shp_enabled and shp_size == 0 then + ingress_lossless_pool_size = size + end end end @@ -165,7 +196,14 @@ for i = 1, #epools, 1 do end end +if shp_enabled and shp_size == 0 then + shp_size = math.ceil(accumulative_xoff / over_subscribe_ratio) +end + local pool_size +if shp_size then + accumulative_occupied_buffer = accumulative_occupied_buffer + shp_size +end if ingress_pool_count == 1 then pool_size = mmu_size - accumulative_occupied_buffer else @@ -176,18 +214,31 @@ if pool_size > ceiling_mmu_size then pool_size = ceiling_mmu_size end +local shp_deployed = false for i = 1, #pools_need_update, 1 do local pool_name = string.match(pools_need_update[i], "BUFFER_POOL|([^%s]+)$") - table.insert(result, pool_name .. ":" .. math.ceil(pool_size)) + if shp_size ~= 0 and pool_name == "ingress_lossless_pool" then + table.insert(result, pool_name .. ":" .. math.ceil(pool_size) .. ":" .. math.ceil(shp_size)) + shp_deployed = true + else + table.insert(result, pool_name .. ":" .. math.ceil(pool_size)) + end +end + +if not shp_deployed and shp_size ~= 0 and ingress_lossless_pool_size ~= nil then + table.insert(result, "ingress_lossless_pool:" .. math.ceil(ingress_lossless_pool_size) .. ":" .. math.ceil(shp_size)) end table.insert(result, "debug:mmu_size:" .. mmu_size) -table.insert(result, "debug:accumulative:" .. accumulative_occupied_buffer) +table.insert(result, "debug:accumulative size:" .. accumulative_occupied_buffer) for i = 1, #statistics do table.insert(result, "debug:" .. statistics[i][1] .. ":" .. statistics[i][2] .. ":" .. statistics[i][3]) end table.insert(result, "debug:extra_400g:" .. (lossypg_reserved_400g - lossypg_reserved) .. ":" .. lossypg_400g) table.insert(result, "debug:mgmt_pool:" .. mgmt_pool_size) table.insert(result, "debug:egress_mirror:" .. accumulative_egress_mirror_overhead) +table.insert(result, "debug:shp_enabled:" .. tostring(shp_enabled)) +table.insert(result, "debug:shp_size:" .. shp_size) +table.insert(result, "debug:accumulative xoff:" .. accumulative_xoff) return result diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 9039aea734..bff7acfd7d 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -274,16 +274,16 @@ string BufferMgrDynamic::getPgPoolMode() } // Meta flows which are called by main flows -void BufferMgrDynamic::calculateHeadroomSize(const string &speed, const string &cable, const string &port_mtu, const string &gearbox_model, buffer_profile_t &headroom) +void BufferMgrDynamic::calculateHeadroomSize(buffer_profile_t &headroom) { // Call vendor-specific lua plugin to calculate the xon, xoff, xon_offset, size and threshold vector keys = {}; vector argv = {}; keys.emplace_back(headroom.name); - argv.emplace_back(speed); - argv.emplace_back(cable); - argv.emplace_back(port_mtu); + argv.emplace_back(headroom.speed); + argv.emplace_back(headroom.cable_length); + argv.emplace_back(headroom.port_mtu); argv.emplace_back(m_identifyGearboxDelay); try @@ -316,6 +316,14 @@ void BufferMgrDynamic::calculateHeadroomSize(const string &speed, const string & } } +// This function is designed to fetch the sizes of shared buffer pool and shared headroom pool +// and programe them to APPL_DB if they differ from the current value. +// The function is called periodically: +// 1. Fetch the sizes by calling lug plugin +// - For each of the pools, it checks the size of shared buffer pool. +// - For ingress_lossless_pool, it checks the size of the shared headroom pool (field xoff of the pool) as well. +// 2. Compare the fetched value and the previous value +// 3. Program to APPL_DB.BUFFER_POOL_TABLE only if its sizes differ from the stored value void BufferMgrDynamic::recalculateSharedBufferPool() { try @@ -326,8 +334,17 @@ void BufferMgrDynamic::recalculateSharedBufferPool() auto ret = runRedisScript(*m_applDb, m_bufferpoolSha, keys, argv); // The format of the result: - // a list of strings containing key, value pairs with colon as separator + // a list of lines containing key, value pairs with colon as separator // each is the size of a buffer pool + // possible format of each line: + // 1. shared buffer pool only: + // : + // eg: "egress_lossless_pool:12800000" + // 2. shared buffer pool and shared headroom pool, for ingress_lossless_pool only: + // ingress_lossless_pool::, + // eg: "ingress_lossless_pool:3200000:1024000" + // 3. debug information: + // debug: for ( auto i : ret) { @@ -336,12 +353,62 @@ void BufferMgrDynamic::recalculateSharedBufferPool() if ("debug" != poolName) { - auto &pool = m_bufferPoolLookup[pairs[0]]; + // We will handle the sizes of buffer pool update here. + // For the ingress_lossless_pool, there are some dedicated steps for shared headroom pool + // - The sizes of both the shared headroom pool and the shared buffer pool should be taken into consideration + // - In case the shared headroom pool size is statically configured, as it is programmed to APPL_DB during buffer pool handling, + // - any change from lua plugin will be ignored. + // - will handle ingress_lossless_pool in the way all other pools are handled in this case + auto &pool = m_bufferPoolLookup[poolName]; + auto &poolSizeStr = pairs[1]; + auto old_xoff = pool.xoff; + bool xoff_updated = false; + + if (poolName == INGRESS_LOSSLESS_PG_POOL_NAME && !isNonZero(m_configuredSharedHeadroomPoolSize)) + { + // Shared headroom pool size is treated as "updated" if either of the following conditions satisfied: + // - It is legal and differs from the stored value. + // - The lua plugin doesn't return the shared headroom pool size but there is a non-zero value stored + // This indicates the shared headroom pool was enabled by over subscribe ratio and is disabled. + // In this case a "0" will programmed to APPL_DB, indicating the shared headroom pool is disabled. + SWSS_LOG_DEBUG("Buffer pool ingress_lossless_pool xoff: %s, size %s", pool.xoff.c_str(), pool.total_size.c_str()); + + if (pairs.size() > 2) + { + auto &xoffStr = pairs[2]; + if (pool.xoff != xoffStr) + { + unsigned long xoffNum = atol(xoffStr.c_str()); + if (m_mmuSizeNumber > 0 && m_mmuSizeNumber < xoffNum) + { + SWSS_LOG_ERROR("Buffer pool %s: Invalid xoff %s, exceeding the mmu size %s, ignored xoff but the pool size will be updated", + poolName.c_str(), xoffStr.c_str(), m_mmuSize.c_str()); + } + else + { + pool.xoff = xoffStr; + xoff_updated = true; + } + } + } + else + { + if (isNonZero(pool.xoff)) + { + xoff_updated = true; + } + pool.xoff = "0"; + } + } - if (pool.total_size == pairs[1]) + // In general, the APPL_DB should be updated in case any of the following conditions satisfied + // 1. Shared headroom pool size has been updated + // This indicates the shared headroom pool is enabled by configuring over subscribe ratio, + // which means the shared headroom pool size has updated by lua plugin + // 2. The size of the shared buffer pool isn't configured and has been updated by lua plugin + if ((pool.total_size == poolSizeStr || !pool.dynamic_size) && !xoff_updated) continue; - auto &poolSizeStr = pairs[1]; unsigned long poolSizeNum = atol(poolSizeStr.c_str()); if (m_mmuSizeNumber > 0 && m_mmuSizeNumber < poolSizeNum) { @@ -350,10 +417,19 @@ void BufferMgrDynamic::recalculateSharedBufferPool() continue; } + auto old_size = pool.total_size; pool.total_size = poolSizeStr; updateBufferPoolToDb(poolName, pool); - SWSS_LOG_NOTICE("Buffer pool %s had been updated with new size [%s]", poolName.c_str(), pool.total_size.c_str()); + if (!pool.xoff.empty()) + { + SWSS_LOG_NOTICE("Buffer pool %s has been updated: size from [%s] to [%s], xoff from [%s] to [%s]", + poolName.c_str(), old_size.c_str(), pool.total_size.c_str(), old_xoff.c_str(), pool.xoff.c_str()); + } + else + { + SWSS_LOG_NOTICE("Buffer pool %s has been updated: size from [%s] to [%s]", poolName.c_str(), old_size.c_str(), pool.total_size.c_str()); + } } else { @@ -409,15 +485,16 @@ void BufferMgrDynamic::updateBufferPoolToDb(const string &name, const buffer_poo vector fvVector; if (pool.ingress) - fvVector.emplace_back(make_pair("type", "ingress")); + fvVector.emplace_back("type", "ingress"); else - fvVector.emplace_back(make_pair("type", "egress")); + fvVector.emplace_back("type", "egress"); - fvVector.emplace_back(make_pair("mode", pool.mode)); + if (!pool.xoff.empty()) + fvVector.emplace_back("xoff", pool.xoff); - SWSS_LOG_INFO("Buffer pool %s is initialized", name.c_str()); + fvVector.emplace_back("mode", pool.mode); - fvVector.emplace_back(make_pair("size", pool.total_size)); + fvVector.emplace_back("size", pool.total_size); m_applBufferPoolTable.set(name, fvVector); @@ -476,7 +553,7 @@ void BufferMgrDynamic::updateBufferPgToDb(const string &key, const string &profi } // We have to check the headroom ahead of applying them -task_process_status BufferMgrDynamic::allocateProfile(const string &speed, const string &cable, const string &mtu, const string &threshold, const string &gearbox_model, string &profile_name) +task_process_status BufferMgrDynamic::allocateProfile(const string &speed, const string &cable_len, const string &mtu, const string &threshold, const string &gearbox_model, string &profile_name) { // Create record in BUFFER_PROFILE table @@ -496,12 +573,16 @@ task_process_status BufferMgrDynamic::allocateProfile(const string &speed, const return task_process_status::task_need_retry; } + profile.speed = speed; + profile.cable_length = cable_len; + profile.port_mtu = mtu; + profile.gearbox_model = gearbox_model; + // Call vendor-specific lua plugin to calculate the xon, xoff, xon_offset, size // Pay attention, the threshold can contain valid value - calculateHeadroomSize(speed, cable, mtu, gearbox_model, profile); + calculateHeadroomSize(profile); profile.threshold = threshold; - profile.dynamic_calculated = true; profile.static_configured = false; profile.lossless = true; profile.name = profile_name; @@ -512,7 +593,7 @@ task_process_status BufferMgrDynamic::allocateProfile(const string &speed, const SWSS_LOG_NOTICE("BUFFER_PROFILE %s has been created successfully", profile_name.c_str()); SWSS_LOG_DEBUG("New profile created %s according to (%s %s %s): xon %s xoff %s size %s", profile_name.c_str(), - speed.c_str(), cable.c_str(), gearbox_model.c_str(), + speed.c_str(), cable_len.c_str(), gearbox_model.c_str(), profile.xon.c_str(), profile.xoff.c_str(), profile.size.c_str()); } else @@ -768,6 +849,106 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string return task_process_status::task_success; } +void BufferMgrDynamic::refreshSharedHeadroomPool(bool enable_state_updated_by_ratio, bool enable_state_updated_by_size) +{ + // The lossless profiles need to be refreshed only if system is switched between SHP and non-SHP + bool need_refresh_profiles = false; + bool shp_enabled_by_size = isNonZero(m_configuredSharedHeadroomPoolSize); + bool shp_enabled_by_ratio = isNonZero(m_overSubscribeRatio); + + /* + * STATE EVENT ACTION + * enabled by size -> config ratio: no action + * enabled by size -> remove ratio: no action + * enabled by size -> remove size: shared headroom pool disabled + * SHP size will be set here (zero) and programmed to APPL_DB in handleBufferPoolTable + * enabled by ratio -> config size: SHP size will be set here (non zero) and programmed to APPL_DB in handleBufferPoolTable + * enabled by ratio -> remove size: SHP size will be set and programmed to APPL_DB during buffer pool size update + * enabled by ratio -> remove ratio: shared headroom pool disabled + * dynamic size: SHP size will be handled and programmed to APPL_DB during buffer pool size update + * static size: SHP size will be set (zero) and programmed to APPL_DB here + * disabled -> config ratio: shared headroom pool enabled. all lossless profiles refreshed + * SHP size will be handled during buffer pool size update + * disabled -> config size: shared headroom pool enabled. all lossless profiles refreshed + * SHP size will be handled here and programmed to APPL_DB during buffer pool table handling + */ + + auto &ingressLosslessPool = m_bufferPoolLookup[INGRESS_LOSSLESS_PG_POOL_NAME]; + if (enable_state_updated_by_ratio) + { + if (shp_enabled_by_size) + { + // enabled by size -> config or remove ratio, no action + SWSS_LOG_INFO("SHP: No need to refresh lossless profiles even if enable state updated by over subscribe ratio, already enabled by SHP size"); + } + else + { + // enabled by ratio -> remove ratio + // disabled -> config ratio + need_refresh_profiles = true; + } + } + + if (enable_state_updated_by_size) + { + if (shp_enabled_by_ratio) + { + // enabled by ratio -> config size, size will be updated (later in this function) + // enabled by ratio -> remove size, no action here, will be handled during buffer pool size update + SWSS_LOG_INFO("SHP: No need to refresh lossless profiles even if enable state updated by SHP size, already enabled by over subscribe ratio"); + } + else + { + // disabled -> config size + // enabled by size -> remove size + need_refresh_profiles = true; + } + } + + if (need_refresh_profiles) + { + SWSS_LOG_NOTICE("Updating dynamic buffer profiles due to shared headroom pool state updated"); + + for (auto it = m_bufferProfileLookup.begin(); it != m_bufferProfileLookup.end(); ++it) + { + auto &name = it->first; + auto &profile = it->second; + if (profile.static_configured) + { + SWSS_LOG_INFO("Non dynamic profile %s skipped", name.c_str()); + continue; + } + SWSS_LOG_INFO("Updating profile %s with speed %s cable length %s mtu %s gearbox model %s", + name.c_str(), + profile.speed.c_str(), profile.cable_length.c_str(), profile.port_mtu.c_str(), profile.gearbox_model.c_str()); + // recalculate the headroom size + calculateHeadroomSize(profile); + if (task_process_status::task_success != doUpdateBufferProfileForSize(profile, false)) + { + SWSS_LOG_ERROR("Failed to update buffer profile %s when toggle shared headroom pool. See previous message for detail. Please adjust the configuration manually", name.c_str()); + } + } + SWSS_LOG_NOTICE("Updating dynamic buffer profiles finished"); + } + + if (shp_enabled_by_size) + { + ingressLosslessPool.xoff = m_configuredSharedHeadroomPoolSize; + if (isNonZero(ingressLosslessPool.total_size)) + updateBufferPoolToDb(INGRESS_LOSSLESS_PG_POOL_NAME, ingressLosslessPool); + } + else if (!shp_enabled_by_ratio && enable_state_updated_by_ratio) + { + // shared headroom pool is enabled by ratio and will be disabled + // need to program APPL_DB because nobody else will take care of it + ingressLosslessPool.xoff = "0"; + if (isNonZero(ingressLosslessPool.total_size)) + updateBufferPoolToDb(INGRESS_LOSSLESS_PG_POOL_NAME, ingressLosslessPool); + } + + checkSharedBufferPoolSize(); +} + // Main flows // Update lossless pg on a port after an PG has been installed on the port @@ -842,13 +1023,13 @@ task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const return task_process_status::task_success; } -task_process_status BufferMgrDynamic::doUpdateStaticProfileTask(buffer_profile_t &profile) +task_process_status BufferMgrDynamic::doUpdateBufferProfileForDynamicTh(buffer_profile_t &profile) { const string &profileName = profile.name; auto &profileToMap = profile.port_pgs; set portsChecked; - if (profile.dynamic_calculated) + if (profile.static_configured && profile.dynamic_calculated) { for (auto &key : profileToMap) { @@ -870,7 +1051,19 @@ task_process_status BufferMgrDynamic::doUpdateStaticProfileTask(buffer_profile_t } } } - else + + checkSharedBufferPoolSize(); + + return task_process_status::task_success; +} + +task_process_status BufferMgrDynamic::doUpdateBufferProfileForSize(buffer_profile_t &profile, bool update_pool_size=true) +{ + const string &profileName = profile.name; + auto &profileToMap = profile.port_pgs; + set portsChecked; + + if (!profile.static_configured || !profile.dynamic_calculated) { for (auto &key : profileToMap) { @@ -883,7 +1076,6 @@ task_process_status BufferMgrDynamic::doUpdateStaticProfileTask(buffer_profile_t if (!isHeadroomResourceValid(port, profile)) { - // to do: get the value from application database SWSS_LOG_ERROR("BUFFER_PROFILE %s cannot be updated because %s referencing it violates the resource limitation", profileName.c_str(), key.c_str()); return task_process_status::task_failed; @@ -895,7 +1087,8 @@ task_process_status BufferMgrDynamic::doUpdateStaticProfileTask(buffer_profile_t updateBufferProfileToDb(profileName, profile); } - checkSharedBufferPoolSize(); + if (update_pool_size) + checkSharedBufferPoolSize(); return task_process_status::task_success; } @@ -931,6 +1124,7 @@ task_process_status BufferMgrDynamic::handleBufferMaxParam(KeyOpFieldsValuesTupl task_process_status BufferMgrDynamic::handleDefaultLossLessBufferParam(KeyOpFieldsValuesTuple &tuple) { string op = kfvOp(tuple); + string newRatio = "0"; if (op == SET_COMMAND) { @@ -939,10 +1133,30 @@ task_process_status BufferMgrDynamic::handleDefaultLossLessBufferParam(KeyOpFiel if (fvField(i) == "default_dynamic_th") { m_defaultThreshold = fvValue(i); - SWSS_LOG_DEBUG("Handling Buffer Maximum value table field default_dynamic_th value %s", m_defaultThreshold.c_str()); + SWSS_LOG_DEBUG("Handling Buffer parameter table field default_dynamic_th value %s", m_defaultThreshold.c_str()); + } + else if (fvField(i) == "over_subscribe_ratio") + { + newRatio = fvValue(i); + SWSS_LOG_DEBUG("Handling Buffer parameter table field over_subscribe_ratio value %s", fvValue(i).c_str()); } } } + else + { + SWSS_LOG_ERROR("Unsupported command %s received for DEFAULT_LOSSLESS_BUFFER_PARAMETER table", op.c_str()); + return task_process_status::task_failed; + } + + if (newRatio != m_overSubscribeRatio) + { + bool isSHPEnabled = isNonZero(m_overSubscribeRatio); + bool willSHPBeEnabled = isNonZero(newRatio); + SWSS_LOG_INFO("Recalculate shared buffer pool size due to over subscribe ratio has been updated from %s to %s", + m_overSubscribeRatio.c_str(), newRatio.c_str()); + m_overSubscribeRatio = newRatio; + refreshSharedHeadroomPool(isSHPEnabled != willSHPBeEnabled, false); + } return task_process_status::task_success; } @@ -1149,6 +1363,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup // 1. Create the corresponding table entries in APPL_DB // 2. Record the table in the internal cache m_bufferPoolLookup buffer_pool_t &bufferPool = m_bufferPoolLookup[pool]; + string newSHPSize = "0"; bufferPool.dynamic_size = true; for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) @@ -1163,7 +1378,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup } if (field == buffer_pool_xoff_field_name) { - bufferPool.xoff = value; + newSHPSize = value; } if (field == buffer_pool_mode_field_name) { @@ -1173,10 +1388,46 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup { bufferPool.ingress = (value == buffer_value_ingress); } - fvVector.emplace_back(FieldValueTuple(field, value)); + fvVector.emplace_back(field, value); SWSS_LOG_INFO("Inserting BUFFER_POOL table field %s value %s", field.c_str(), value.c_str()); } - if (!bufferPool.dynamic_size) + + bool dontUpdatePoolToDb = bufferPool.dynamic_size; + if (pool == INGRESS_LOSSLESS_PG_POOL_NAME) + { + /* + * "dontUpdatPoolToDb" is calculated for ingress_lossless_pool according to following rules: + * Don't update | pool size | SHP enabled by size | SHP enabled by over subscribe ratio + * True | Dynamic | Any | Any + * False | Static | True | Any + * True | Static | False | True + * False | Static | False | False + */ + bool willSHPBeEnabledBySize = isNonZero(newSHPSize); + if (newSHPSize != m_configuredSharedHeadroomPoolSize) + { + bool isSHPEnabledBySize = isNonZero(m_configuredSharedHeadroomPoolSize); + + m_configuredSharedHeadroomPoolSize = newSHPSize; + refreshSharedHeadroomPool(false, isSHPEnabledBySize != willSHPBeEnabledBySize); + } + else if (!newSHPSize.empty()) + { + SWSS_LOG_INFO("Shared headroom pool size updated without change (new %s vs current %s), skipped", newSHPSize.c_str(), m_configuredSharedHeadroomPoolSize.c_str()); + } + + if (!willSHPBeEnabledBySize) + { + // Don't need to program APPL_DB if shared headroom pool is enabled + dontUpdatePoolToDb |= isNonZero(m_overSubscribeRatio); + } + } + else if (isNonZero(newSHPSize)) + { + SWSS_LOG_ERROR("Field xoff is supported for %s only, but got for %s, ignored", INGRESS_LOSSLESS_PG_POOL_NAME, pool.c_str()); + } + + if (!dontUpdatePoolToDb) { m_applBufferPoolTable.set(pool, fvVector); m_stateBufferPoolTable.set(pool, fvVector); @@ -1299,12 +1550,12 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues profileApp.state = PROFILE_NORMAL; SWSS_LOG_NOTICE("BUFFER_PROFILE %s is dynamic calculation so it won't be deployed to APPL_DB until referenced by a port", profileName.c_str()); - doUpdateStaticProfileTask(profileApp); + doUpdateBufferProfileForDynamicTh(profileApp); } else { profileApp.state = PROFILE_NORMAL; - doUpdateStaticProfileTask(profileApp); + doUpdateBufferProfileForSize(profileApp); SWSS_LOG_NOTICE("BUFFER_PROFILE %s has been inserted into APPL_DB", profileName.c_str()); SWSS_LOG_DEBUG("BUFFER_PROFILE %s for headroom override has been stored internally: [pool %s xon %s xoff %s size %s]", profileName.c_str(), diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index 941491e7b6..f11bd86201 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -47,6 +47,14 @@ typedef struct { bool static_configured; bool ingress; bool lossless; + + // fields representing parameters by which the headroom is calculated + std::string speed; + std::string cable_length; + std::string port_mtu; + std::string gearbox_model; + + // APPL_DB.BUFFER_PROFILE fields std::string name; std::string size; std::string xon; @@ -124,6 +132,8 @@ class BufferMgrDynamic : public Orch bool m_portInitDone; bool m_firstTimeCalculateBufferPool; + std::string m_configuredSharedHeadroomPoolSize; + std::shared_ptr m_applDb = nullptr; SelectableTimer *m_buffermgrPeriodtimer = nullptr; @@ -191,6 +201,8 @@ class BufferMgrDynamic : public Orch unsigned long m_mmuSizeNumber; std::string m_defaultThreshold; + std::string m_overSubscribeRatio; + // Initializers void initTableHandlerMap(); void parseGearboxInfo(std::shared_ptr> gearboxInfo); @@ -202,6 +214,10 @@ class BufferMgrDynamic : public Orch std::string parseObjectNameFromKey(const std::string &key, size_t pos/* = 1*/); std::string parseObjectNameFromReference(const std::string &reference); std::string getDynamicProfileName(const std::string &speed, const std::string &cable, const std::string &mtu, const std::string &threshold, const std::string &gearbox_model); + inline bool isNonZero(const std::string &value) const + { + return !value.empty() && value != "0"; + } // APPL_DB table operations void updateBufferPoolToDb(const std::string &name, const buffer_pool_t &pool); @@ -209,19 +225,21 @@ class BufferMgrDynamic : public Orch void updateBufferPgToDb(const std::string &key, const std::string &profile, bool add); // Meta flows - void calculateHeadroomSize(const std::string &speed, const std::string &cable, const std::string &port_mtu, const std::string &gearbox_model, buffer_profile_t &headroom); + void calculateHeadroomSize(buffer_profile_t &headroom); void checkSharedBufferPoolSize(); void recalculateSharedBufferPool(); task_process_status allocateProfile(const std::string &speed, const std::string &cable, const std::string &mtu, const std::string &threshold, const std::string &gearbox_model, std::string &profile_name); void releaseProfile(const std::string &profile_name); bool isHeadroomResourceValid(const std::string &port, const buffer_profile_t &profile, const std::string &new_pg); + void refreshSharedHeadroomPool(bool enable_state_updated_by_ratio, bool enable_state_updated_by_size); // Main flows task_process_status refreshPriorityGroupsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); task_process_status doUpdatePgTask(const std::string &pg_key, const std::string &port); task_process_status doRemovePgTask(const std::string &pg_key, const std::string &port); task_process_status doAdminStatusTask(const std::string port, const std::string adminStatus); - task_process_status doUpdateStaticProfileTask(buffer_profile_t &profile); + task_process_status doUpdateBufferProfileForDynamicTh(buffer_profile_t &profile); + task_process_status doUpdateBufferProfileForSize(buffer_profile_t &profile, bool update_pool_size); // Table update handlers task_process_status handleBufferMaxParam(KeyOpFieldsValuesTuple &t); diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index b0af416c41..932247de37 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -408,3 +408,95 @@ def test_nonDefaultAlpha(self, dvs, testlog): # clear configuration self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic') + + def test_sharedHeadroomPool(self, dvs, testlog): + self.setup_db(dvs) + + # configure lossless PG 3-4 on interface and start up the interface + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + dvs.runcmd('config interface startup Ethernet0') + + expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + profileInApplDb = self.app_db.get_entry('BUFFER_PROFILE_TABLE', expectedProfile) + + # enable shared headroom pool by configuring over subscribe ratio + default_lossless_buffer_parameter = self.config_db.get_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE') + over_subscribe_ratio = default_lossless_buffer_parameter.get('over_subscribe_ratio') + assert not over_subscribe_ratio or over_subscribe_ratio == '0', "Over subscribe ratio isn't 0" + + # config over subscribe ratio to 2 + default_lossless_buffer_parameter['over_subscribe_ratio'] = '2' + self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter) + + # check buffer profile: xoff should be removed from size + profileInApplDb['size'] = profileInApplDb['xon'] + self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + + # check ingress_lossless_pool between appldb and asicdb + # there are only two lossless PGs configured on one port. + # hence the shared headroom pool size should be pg xoff * 2 / over subscribe ratio (2) = xoff. + ingress_lossless_pool_in_appldb = self.app_db.get_entry('BUFFER_POOL_TABLE', 'ingress_lossless_pool') + shp_size = profileInApplDb['xoff'] + ingress_lossless_pool_in_appldb['xoff'] = shp_size + # toggle shared headroom pool, it requires some time to update pools + time.sleep(20) + self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb) + ingress_lossless_pool_in_asicdb = self.asic_db.get_entry('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE', self.ingress_lossless_pool_oid) + ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size + self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb) + + # config shared headroom pool size + shp_size = '204800' + ingress_lossless_pool_in_configdb = self.config_db.get_entry('BUFFER_POOL', 'ingress_lossless_pool') + ingress_lossless_pool_in_configdb['xoff'] = shp_size + self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool', ingress_lossless_pool_in_configdb) + # make sure the size is still equal to xon in the profile + self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + + # config over subscribe ratio to 4 + default_lossless_buffer_parameter['over_subscribe_ratio'] = '4' + self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter) + # shp size wins in case both size and over subscribe ratio is configured + ingress_lossless_pool_in_appldb['xoff'] = shp_size + self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb) + ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size + self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb) + # make sure the size is still equal to xon in the profile + self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + + # remove size configuration, new over subscribe ratio takes effect + ingress_lossless_pool_in_configdb['xoff'] = '0' + self.config_db.update_entry('BUFFER_POOL', 'ingress_lossless_pool', ingress_lossless_pool_in_configdb) + # shp size: pg xoff * 2 / over subscribe ratio (4) = pg xoff / 2 + shp_size = str(int(int(profileInApplDb['xoff']) / 2)) + time.sleep(30) + ingress_lossless_pool_in_appldb['xoff'] = shp_size + self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb) + ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = shp_size + self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb) + # make sure the size is still equal to xon in the profile + self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + + # remove over subscribe ratio configuration + default_lossless_buffer_parameter['over_subscribe_ratio'] = '0' + self.config_db.update_entry('DEFAULT_LOSSLESS_BUFFER_PARAMETER', 'AZURE', default_lossless_buffer_parameter) + # check whether shp size has been removed from both asic db and appl db + ingress_lossless_pool_in_appldb['xoff'] = '0' + self.app_db.wait_for_field_match('BUFFER_POOL_TABLE', 'ingress_lossless_pool', ingress_lossless_pool_in_appldb) + ingress_lossless_pool_in_asicdb['SAI_BUFFER_POOL_ATTR_XOFF_SIZE'] = '0' + self.asic_db.wait_for_field_match('ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_POOL', self.ingress_lossless_pool_oid, ingress_lossless_pool_in_asicdb) + # make sure the size is equal to xon + xoff in the profile + profileInApplDb['size'] = str(int(profileInApplDb['xon']) + int(profileInApplDb['xoff'])) + self.app_db.wait_for_field_match('BUFFER_PROFILE_TABLE', expectedProfile, profileInApplDb) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + + # remove lossless PG 3-4 on interface + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + dvs.runcmd('config interface shutdown Ethernet0') From 189a9641cf47e81c7c90d2501e70b73b74b082e1 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Mon, 8 Feb 2021 05:22:09 +0800 Subject: [PATCH 06/54] [buffermgr] Support maximum port headroom checking (#1607) * Support maximum port headroom checking - Fetch the maximum port headroom via fetching the port attribute SAI_PORT_ATTR_QOS_MAXIMUM_HEADROOM_SIZE when the orchagent starts and push the data into STATE_DB - Check the accumulative port headroom against the maximum headroom in buffer_check_headroom_.lua On Mellanox platform, this PR depends on PR #6566 to be merged. In that PR the required SAI attribute is supported. On other platforms, there is no dependency. Signed-off-by: Stephen Sun stephens@nvidia.com Why I did it On some platforms, the SAI will notify orchagent shut down if a headroom size that causes the accumulative port headroom to exceed its limit is programmed to SAI. To avoid that, we need to check this before programming it to SAI. How I verified it Run the regression test. --- cfgmgr/buffer_check_headroom_mellanox.lua | 28 ++--------------------- orchagent/port.h | 1 + orchagent/portsorch.cpp | 23 +++++++++++++++++++ orchagent/portsorch.h | 3 +++ 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/cfgmgr/buffer_check_headroom_mellanox.lua b/cfgmgr/buffer_check_headroom_mellanox.lua index da4a443b1b..510a967b73 100644 --- a/cfgmgr/buffer_check_headroom_mellanox.lua +++ b/cfgmgr/buffer_check_headroom_mellanox.lua @@ -10,36 +10,15 @@ local new_pg = ARGV[3] local accumulative_size = 0 local appl_db = "0" -local config_db = "4" local state_db = "6" local ret_true = {} -local ret_false = {} local ret = {} local default_ret = {} table.insert(ret_true, "result:true") -table.insert(ret_false, "result:false") --- Fetch the cable length from CONFIG_DB -redis.call('SELECT', config_db) -local cable_length_keys = redis.call('KEYS', 'CABLE_LENGTH*') -if #cable_length_keys == 0 then - return ret_true -end - --- Check whether cable length exceeds 300m (maximum value in the non-dynamic-buffer solution) -local cable_length_str = redis.call('HGET', cable_length_keys[1], port) -if cable_length_str == nil then - return ret_true -end -local cable_length = tonumber(string.sub(cable_length_str, 1, -2)) -if cable_length > 300 then - default_ret = ret_false -else - default_ret = ret_true -end -table.insert(default_ret, 'debug:no max_headroom_size configured, check cable length instead') +default_ret = ret_true local speed = redis.call('HGET', 'PORT|' .. port, 'speed') @@ -67,7 +46,6 @@ local function get_number_of_pgs(keyname) local range = string.match(keyname, "Ethernet%d+:([^%s]+)$") local size if range == nil then - table.insert(debuginfo, "debug:invalid pg:" .. keyname) return 0 end if string.len(range) == 1 then @@ -119,11 +97,9 @@ if max_headroom_size > accumulative_size then table.insert(ret, "result:true") else table.insert(ret, "result:false") + table.insert(ret, "debug:Accumulative headroom on port " .. accumulative_size .. " exceeds the maximum available headroom which is " .. max_headroom_size) end -table.insert(ret, "debug:max headroom:" .. max_headroom_size) -table.insert(ret, "debug:accumulative headroom:" .. accumulative_size) - for i = 1, #debuginfo do table.insert(ret, debuginfo[i]) end diff --git a/orchagent/port.h b/orchagent/port.h index baf8e3046e..35085c16c1 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -124,6 +124,7 @@ class Port uint32_t m_vnid = VNID_NONE; uint32_t m_fdb_count = 0; uint32_t m_up_member_count = 0; + uint32_t m_maximum_headroom = 0; /* * Following two bit vectors are used to lock diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 334cd68f18..8f2902c77e 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -257,6 +257,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); + m_state_db = shared_ptr(new DBConnector("STATE_DB", 0)); + m_stateBufferMaximumValueTable = unique_ptr
(new Table(m_state_db.get(), STATE_BUFFER_MAXIMUM_VALUE_TABLE)); + initGearbox(); string queueWmSha, pgWmSha; @@ -3291,6 +3294,25 @@ void PortsOrch::initializePriorityGroups(Port &port) SWSS_LOG_INFO("Get priority groups for port %s", port.m_alias.c_str()); } +void PortsOrch::initializePortMaximumHeadroom(Port &port) +{ + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_QOS_MAXIMUM_HEADROOM_SIZE; + + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Unable to get the maximum headroom for port %s rv:%d, ignored", port.m_alias.c_str(), status); + return; + } + + vector fvVector; + port.m_maximum_headroom = attr.value.u32; + fvVector.emplace_back("max_headroom_size", to_string(port.m_maximum_headroom)); + m_stateBufferMaximumValueTable->set(port.m_alias, fvVector); +} + bool PortsOrch::initializePort(Port &port) { SWSS_LOG_ENTER(); @@ -3299,6 +3321,7 @@ bool PortsOrch::initializePort(Port &port) initializePriorityGroups(port); initializeQueues(port); + initializePortMaximumHeadroom(port); /* Create host interface */ if (!addHostIntfs(port, port.m_alias, port.m_hif_id)) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index ecf65c4f8e..e24ecb9450 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -155,6 +155,7 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_pgTable; unique_ptr
m_pgPortTable; unique_ptr
m_pgIndexTable; + unique_ptr
m_stateBufferMaximumValueTable; unique_ptr m_flexCounterTable; unique_ptr m_flexCounterGroupTable; @@ -164,6 +165,7 @@ class PortsOrch : public Orch, public Subject shared_ptr m_counter_db; shared_ptr m_flex_db; + shared_ptr m_state_db; FlexCounterManager port_stat_manager; FlexCounterManager port_buffer_drop_stat_manager; @@ -226,6 +228,7 @@ class PortsOrch : public Orch, public Subject bool initializePort(Port &port); void initializePriorityGroups(Port &port); + void initializePortMaximumHeadroom(Port &port); void initializeQueues(Port &port); bool addHostIntfs(Port &port, string alias, sai_object_id_t &host_intfs_id); From 6554b5d6c57e19fe0c4d633ca8b644da22b26bb1 Mon Sep 17 00:00:00 2001 From: Kamil Cudnik Date: Mon, 8 Feb 2021 18:50:02 +0100 Subject: [PATCH 07/54] [tests] Remove legacy saiattributelist.h dependency (#1608) This file was refactored in sairedis repo to SaiAttributeList.h proper class --- tests/mock_tests/aclorch_ut.cpp | 2 ++ tests/mock_tests/check.h | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index f3ca17c865..302f894747 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -23,6 +23,8 @@ extern sai_route_api_t *sai_route_api; extern sai_next_hop_group_api_t* sai_next_hop_group_api; extern string gMySwitchType; +using namespace saimeta; + namespace aclorch_test { using namespace std; diff --git a/tests/mock_tests/check.h b/tests/mock_tests/check.h index 3eddcecc2c..d1b095562d 100644 --- a/tests/mock_tests/check.h +++ b/tests/mock_tests/check.h @@ -4,11 +4,17 @@ #include #include -#include "saiattributelist.h" +#include "SaiAttributeList.h" + +#include +#include "swss/dbconnector.h" +#include "swss/logger.h" +#include "sai_serialize.h" +#include "string.h" struct Check { - static bool AttrListEq(sai_object_type_t objecttype, const std::vector &act_attr_list, SaiAttributeList &exp_attr_list) + static bool AttrListEq(sai_object_type_t objecttype, const std::vector &act_attr_list, saimeta::SaiAttributeList &exp_attr_list) { if (act_attr_list.size() != exp_attr_list.get_attr_count()) { From b8193d1de5c8ba4614354cda9761d3e251340c70 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Tue, 9 Feb 2021 10:41:49 -0800 Subject: [PATCH 08/54] Migrate serdes programming to port serdes object (#1611) Co-authored-by: dgsudharsan --- orchagent/portsorch.cpp | 223 +++++++++++++++++++++++++++------------- orchagent/portsorch.h | 8 +- 2 files changed, 158 insertions(+), 73 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 8f2902c77e..905ffc52e1 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -1986,6 +1986,9 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) /* remove port name map from counter table */ m_counter_db->hdel(COUNTERS_PORT_NAME_MAP, alias); + /* Remove the associated port serdes attribute */ + removePortSerdesAttribute(p.m_port_id); + m_portList[alias].m_init = false; SWSS_LOG_NOTICE("De-Initialized port %s", alias.c_str()); } @@ -2144,9 +2147,9 @@ void PortsOrch::doPortTask(Consumer &consumer) if (op == SET_COMMAND) { set lane_set; - vector pre_emphasis; - vector idriver; - vector ipredriver; + vector attr_val; + map> serdes_attr; + typedef pair> serdes_attr_pair; string admin_status; string fec_mode; string pfc_asym; @@ -2158,14 +2161,14 @@ void PortsOrch::doPortTask(Consumer &consumer) for (auto i : kfvFieldsValues(t)) { + attr_val.clear(); /* Set interface index */ if (fvField(i) == "index") { index = (int)stoul(fvValue(i)); } - /* Get lane information of a physical port and initialize the port */ - if (fvField(i) == "lanes") + else if (fvField(i) == "lanes") { string lane_str; istringstream iss(fvValue(i)); @@ -2175,65 +2178,107 @@ void PortsOrch::doPortTask(Consumer &consumer) int lane = stoi(lane_str); lane_set.insert(lane); } - } - /* Set port admin status */ - if (fvField(i) == "admin_status") + else if (fvField(i) == "admin_status") { admin_status = fvValue(i); } - /* Set port MTU */ - if (fvField(i) == "mtu") + else if (fvField(i) == "mtu") { mtu = (uint32_t)stoul(fvValue(i)); } - /* Set port speed */ - if (fvField(i) == "speed") + else if (fvField(i) == "speed") { speed = (uint32_t)stoul(fvValue(i)); } - /* Set port fec */ - if (fvField(i) == "fec") + else if (fvField(i) == "fec") { fec_mode = fvValue(i); } - /* Get port fdb learn mode*/ - if (fvField(i) == "learn_mode") + else if (fvField(i) == "learn_mode") { learn_mode = fvValue(i); } - /* Set port asymmetric PFC */ - if (fvField(i) == "pfc_asym") + else if (fvField(i) == "pfc_asym") + { pfc_asym = fvValue(i); - + } /* Set autoneg and ignore the port speed setting */ - if (fvField(i) == "autoneg") + else if (fvField(i) == "autoneg") { an = (int)stoul(fvValue(i)); } - /* Set port serdes Pre-emphasis */ - if (fvField(i) == "preemphasis") + else if (fvField(i) == "preemphasis") { - getPortSerdesVal(fvValue(i), pre_emphasis); + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_PREEMPHASIS, attr_val)); } - /* Set port serdes idriver */ - if (fvField(i) == "idriver") + else if (fvField(i) == "idriver") { - getPortSerdesVal(fvValue(i), idriver); + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_IDRIVER, attr_val)); } - /* Set port serdes ipredriver */ - if (fvField(i) == "ipredriver") + else if (fvField(i) == "ipredriver") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_IPREDRIVER, attr_val)); + } + /* Set port serdes pre1 */ + else if (fvField(i) == "pre1") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE1, attr_val)); + } + /* Set port serdes pre2 */ + else if (fvField(i) == "pre2") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE2, attr_val)); + } + /* Set port serdes pre3 */ + else if (fvField(i) == "pre3") { - getPortSerdesVal(fvValue(i), ipredriver); + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_PRE3, attr_val)); + } + /* Set port serdes main */ + else if (fvField(i) == "main") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_MAIN, attr_val)); + } + /* Set port serdes post1 */ + else if (fvField(i) == "post1") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST1, attr_val)); + } + /* Set port serdes post2 */ + else if (fvField(i) == "post2") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST2, attr_val)); + } + /* Set port serdes post3 */ + else if (fvField(i) == "post3") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_POST3, attr_val)); + } + /* Set port serdes attn */ + else if (fvField(i) == "attn") + { + getPortSerdesVal(fvValue(i), attr_val); + serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_TX_FIR_ATTN, attr_val)); } } @@ -2554,9 +2599,9 @@ void PortsOrch::doPortTask(Consumer &consumer) } } - if (pre_emphasis.size() != 0) + if (serdes_attr.size() != 0) { - if (setPortSerdesAttribute(p.m_port_id, SAI_PORT_ATTR_SERDES_PREEMPHASIS, pre_emphasis)) + if (setPortSerdesAttribute(p.m_port_id, serdes_attr)) { SWSS_LOG_NOTICE("Set port %s preemphasis is success", alias.c_str()); } @@ -2569,36 +2614,6 @@ void PortsOrch::doPortTask(Consumer &consumer) } - if (idriver.size() != 0) - { - if (setPortSerdesAttribute(p.m_port_id, SAI_PORT_ATTR_SERDES_IDRIVER, idriver)) - { - SWSS_LOG_NOTICE("Set port %s idriver is success", alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to set port %s idriver", alias.c_str()); - it++; - continue; - } - - } - - if (ipredriver.size() != 0) - { - if (setPortSerdesAttribute(p.m_port_id, SAI_PORT_ATTR_SERDES_IPREDRIVER, ipredriver)) - { - SWSS_LOG_NOTICE("Set port %s ipredriver is success", alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to set port %s ipredriver", alias.c_str()); - it++; - continue; - } - - } - /* Last step set port admin status */ if (!admin_status.empty() && (p.m_admin_state_up != (admin_status == "up"))) { @@ -4449,29 +4464,95 @@ bool PortsOrch::removeAclTableGroup(const Port &p) return true; } -bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, sai_attr_id_t attr_id, - vector &serdes_val) +bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, + map> &serdes_attr) { SWSS_LOG_ENTER(); - sai_attribute_t attr; + vector attr_list; + sai_attribute_t port_attr; + sai_attribute_t port_serdes_attr; + sai_status_t status; + sai_object_id_t port_serdes_id = SAI_NULL_OBJECT_ID; + + port_attr.id = SAI_PORT_ATTR_PORT_SERDES_ID; + status = sai_port_api->get_port_attribute(port_id, 1, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get port attr serdes id %d to port pid:0x%" PRIx64, + port_attr.id, port_id); + return false; + } + + if (port_attr.value.oid != SAI_NULL_OBJECT_ID) + { + status = sai_port_api->remove_port_serdes(port_attr.value.oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove existing port serdes attr 0x%" PRIx64 " port 0x%" PRIx64, + port_attr.value.oid, port_id); + return false; + } + } - memset(&attr, 0, sizeof(attr)); - attr.id = attr_id; - attr.value.u32list.count = (uint32_t)serdes_val.size(); - attr.value.u32list.list = serdes_val.data(); + port_serdes_attr.id = SAI_PORT_SERDES_ATTR_PORT_ID; + port_serdes_attr.value.oid = port_id; + attr_list.emplace_back(port_serdes_attr); + SWSS_LOG_INFO("Creating serdes for port 0x%" PRIx64, port_id); + + for (auto it = serdes_attr.begin(); it != serdes_attr.end(); it++) + { + port_serdes_attr.id = it->first; + port_serdes_attr.value.u32list.count = (uint32_t)it->second.size(); + port_serdes_attr.value.u32list.list = it->second.data(); + attr_list.emplace_back(port_serdes_attr); + } + status = sai_port_api->create_port_serdes(&port_serdes_id, gSwitchId, + static_cast(serdes_attr.size()+1), + attr_list.data()); - sai_status_t status = sai_port_api->set_port_attribute(port_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set serdes attribute %d to port pid:%" PRIx64, - attr_id, port_id); + SWSS_LOG_ERROR("Failed to create port serdes for port 0x%" PRIx64, + port_id); return false; } + SWSS_LOG_NOTICE("Created port serdes object 0x%" PRIx64 " for port 0x%" PRIx64, port_serdes_id, port_id); return true; } +void PortsOrch::removePortSerdesAttribute(sai_object_id_t port_id) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t port_attr; + sai_status_t status; + sai_object_id_t port_serdes_id = SAI_NULL_OBJECT_ID; + + port_attr.id = SAI_PORT_ATTR_PORT_SERDES_ID; + status = sai_port_api->get_port_attribute(port_id, 1, &port_attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_DEBUG("Failed to get port attr serdes id %d to port pid:0x%" PRIx64, + port_attr.id, port_id); + return; + } + + if (port_attr.value.oid != SAI_NULL_OBJECT_ID) + { + status = sai_port_api->remove_port_serdes(port_attr.value.oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove existing port serdes attr 0x%" PRIx64 " port 0x%" PRIx64, + port_attr.value.oid, port_id); + return; + } + } + SWSS_LOG_NOTICE("Removed port serdes object 0x%" PRIx64 " for port 0x%" PRIx64, port_serdes_id, port_id); +} + void PortsOrch::getPortSerdesVal(const std::string& val_str, std::vector &lane_values) { diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index e24ecb9450..d692b17661 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -286,8 +286,12 @@ class PortsOrch : public Orch, public Subject void getPortSerdesVal(const std::string& s, std::vector &lane_values); - bool setPortSerdesAttribute(sai_object_id_t port_id, sai_attr_id_t attr_id, - vector &serdes_val); + bool setPortSerdesAttribute(sai_object_id_t port_id, + std::map> &serdes_attr); + + + void removePortSerdesAttribute(sai_object_id_t port_id); + bool getSaiAclBindPointType(Port::Type type, sai_acl_bind_point_type_t &sai_acl_bind_type); void initGearbox(); From aaa7bf2d1ec04fbdbc835f9139d7a76061beb740 Mon Sep 17 00:00:00 2001 From: Vaibhav Hemant Dixit Date: Wed, 10 Feb 2021 04:54:14 -0800 Subject: [PATCH 09/54] Log level change from ERR to INFO for fetch systemports issue (#1632) SAI attribute SAI_SWITCH_ATTR_NUMBER_OF_SYSTEM_PORTS is not supported on many platforms, and the call to getSystemPorts() throws error. This error is not harmful at this state, and therefore the log level is changed from ERROR to INFO. --- orchagent/portsorch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 905ffc52e1..c845cafba8 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -4848,7 +4848,7 @@ bool PortsOrch::getSystemPorts() status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to get number of system ports, rv:%d", status); + SWSS_LOG_INFO("Failed to get number of system ports, rv:%d", status); return false; } From 8739791455b02d2b98ecce0966c67b81f8ca5ee1 Mon Sep 17 00:00:00 2001 From: lguohan Date: Wed, 10 Feb 2021 08:37:30 -0800 Subject: [PATCH 10/54] [ci]: use build template (#1633) Signed-off-by: Guohan Lu --- .azure-pipelines/build-template.yml | 100 +++++++++++++ azure-pipelines.yml | 223 ++++------------------------ 2 files changed, 131 insertions(+), 192 deletions(-) create mode 100644 .azure-pipelines/build-template.yml diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml new file mode 100644 index 0000000000..7d8e12c3ab --- /dev/null +++ b/.azure-pipelines/build-template.yml @@ -0,0 +1,100 @@ +parameters: +- name: arch + type: string + values: + - amd64 + - armhf + - arm64 + +- name: pool + type: string + values: + - sonicbld + - default + default: default + +- name: timeout + type: number + default: 60 + +- name: sonic_slave + type: string + +- name: sairedis_artifact_name + type: string + +- name: swss_common_artifact_name + type: string + +- name: artifact_name + type: string + +jobs: +- job: + displayName: ${{ parameters.arch }} + timeoutInMinutes: ${{ parameters.timeout }} + + pool: + ${{ if ne(parameters.pool, 'default') }}: + name: ${{ parameters.pool }} + ${{ if eq(parameters.pool, 'default') }}: + vmImage: 'ubuntu-20.04' + + container: + image: sonicdev-microsoft.azurecr.io:443/${{ parameters.sonic_slave }}:latest + + steps: + - script: | + sudo apt-get install -y libhiredis0.14 libhiredis-dev + sudo apt-get install -y libzmq5 libzmq3-dev + sudo apt-get install -qq -y \ + libhiredis-dev \ + libnl-3-dev \ + libnl-genl-3-dev \ + libnl-route-3-dev \ + libnl-nf-3-dev \ + swig3.0 + sudo apt-get install -y libdbus-1-3 + sudo apt-get install -y libteam-dev \ + libteam5 \ + libteamdctl0 + displayName: "Install dependencies" + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 9 + artifacts: ${{ parameters.swss_common_artifact_name }} + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic swss common deb packages" + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 12 + artifacts: ${{ parameters.sairedis_artifact_name }} + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic sairedis deb packages" + - script: | + sudo dpkg -i ${{ parameters.swss_common_artifact_name }}/libswsscommon_1.0.0_${{ parameters.arch }}.deb + sudo dpkg -i ${{ parameters.swss_common_artifact_name }}/libswsscommon-dev_1.0.0_${{ parameters.arch }}.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsaivs_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsaivs-dev_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsairedis_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsairedis-dev_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsaimetadata_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/libsaimetadata-dev_*.deb + sudo dpkg -i ${{ parameters.sairedis_artifact_name }}/syncd-vs_*.deb + workingDirectory: $(Pipeline.Workspace) + displayName: "Install sonic swss common and sairedis" + - checkout: self + submodules: true + - script: | + ./autogen.sh + dpkg-buildpackage -us -uc -b -j$(nproc) && cp ../*.deb . + displayName: "Compile sonic swss" + - publish: $(System.DefaultWorkingDirectory)/ + artifact: ${{ parameters.artifact_name }} + displayName: "Archive swss debian packages" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 852253aa49..af3222caec 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,195 +8,34 @@ trigger: include: - "*" -jobs: -- job: - displayName: "amd64" - pool: - vmImage: 'ubuntu-20.04' - - container: - image: sonicdev-microsoft.azurecr.io:443/sonic-slave-buster:latest - - steps: - - script: | - sudo apt-get install -y libhiredis0.14 libhiredis-dev - sudo apt-get install -y libzmq5 libzmq3-dev - sudo apt-get install -qq -y \ - libhiredis-dev \ - libnl-3-dev \ - libnl-genl-3-dev \ - libnl-route-3-dev \ - libnl-nf-3-dev \ - swig3.0 - sudo apt-get install -y libdbus-1-3 - sudo apt-get install -y libteam-dev \ - libteam5 \ - libteamdctl0 - displayName: "Install dependencies" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 9 - artifacts: sonic-swss-common - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 12 - artifacts: sonic-sairedis - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic sairedis deb packages" - - script: | - sudo dpkg -i sonic-swss-common/libswsscommon_1.0.0_amd64.deb - sudo dpkg -i sonic-swss-common/libswsscommon-dev_1.0.0_amd64.deb - sudo dpkg -i sonic-sairedis/libsaivs_*.deb - sudo dpkg -i sonic-sairedis/libsaivs-dev_*.deb - sudo dpkg -i sonic-sairedis/libsairedis_*.deb - sudo dpkg -i sonic-sairedis/libsairedis-dev_*.deb - sudo dpkg -i sonic-sairedis/libsaimetadata_*.deb - sudo dpkg -i sonic-sairedis/libsaimetadata-dev_*.deb - sudo dpkg -i sonic-sairedis/syncd-vs_*.deb - workingDirectory: $(Pipeline.Workspace) - displayName: "Install sonic swss common and sairedis" - - checkout: self - submodules: true - - script: | - ./autogen.sh - dpkg-buildpackage -us -uc -b -j$(nproc) && cp ../*.deb . - displayName: "Compile sonic swss" - - publish: $(System.DefaultWorkingDirectory)/ - artifact: sonic-swss - displayName: "Archive swss debian packages" - -- job: - displayName: "arm64" - timeoutInMinutes: 180 - pool: sonicbld - - container: - image: sonicdev-microsoft.azurecr.io:443/sonic-slave-buster-arm64:latest - - steps: - - script: | - sudo apt-get install -y libhiredis0.14 libhiredis-dev - sudo apt-get install -y libzmq5 libzmq3-dev - sudo apt-get install -qq -y \ - libhiredis-dev \ - libnl-3-dev \ - libnl-genl-3-dev \ - libnl-route-3-dev \ - libnl-nf-3-dev \ - swig3.0 - sudo apt-get install -y libdbus-1-3 - sudo apt-get install -y libteam-dev \ - libteam5 \ - libteamdctl0 - displayName: "Install dependencies" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 9 - artifacts: sonic-swss-common.arm64 - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 12 - artifacts: sonic-sairedis.arm64 - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic sairedis deb packages" - - script: | - sudo dpkg -i sonic-swss-common.arm64/libswsscommon_1.0.0_arm64.deb - sudo dpkg -i sonic-swss-common.arm64/libswsscommon-dev_1.0.0_arm64.deb - sudo dpkg -i sonic-sairedis.arm64/libsaivs_*.deb - sudo dpkg -i sonic-sairedis.arm64/libsaivs-dev_*.deb - sudo dpkg -i sonic-sairedis.arm64/libsairedis_*.deb - sudo dpkg -i sonic-sairedis.arm64/libsairedis-dev_*.deb - sudo dpkg -i sonic-sairedis.arm64/libsaimetadata_*.deb - sudo dpkg -i sonic-sairedis.arm64/libsaimetadata-dev_*.deb - sudo dpkg -i sonic-sairedis.arm64/syncd-vs_*.deb - workingDirectory: $(Pipeline.Workspace) - displayName: "Install sonic swss common and sairedis" - - checkout: self - submodules: true - - script: | - ./autogen.sh - dpkg-buildpackage -us -uc -b -j$(nproc) && cp ../*.deb . - displayName: "Compile sonic swss" - - publish: $(System.DefaultWorkingDirectory)/ - artifact: sonic-swss.arm64 - displayName: "Archive swss debian packages" - -- job: - displayName: "armhf" - timeoutInMinutes: 180 - pool: sonicbld - - container: - image: sonicdev-microsoft.azurecr.io:443/sonic-slave-buster-armhf:latest - - steps: - - script: | - sudo apt-get install -y libhiredis0.14 libhiredis-dev - sudo apt-get install -y libzmq5 libzmq3-dev - sudo apt-get install -qq -y \ - libhiredis-dev \ - libnl-3-dev \ - libnl-genl-3-dev \ - libnl-route-3-dev \ - libnl-nf-3-dev \ - swig3.0 - sudo apt-get install -y libdbus-1-3 - sudo apt-get install -y libteam-dev \ - libteam5 \ - libteamdctl0 - displayName: "Install dependencies" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 9 - artifacts: sonic-swss-common.armhf - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic swss common deb packages" - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 12 - artifacts: sonic-sairedis.armhf - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/master' - displayName: "Download sonic sairedis deb packages" - - script: | - sudo dpkg -i sonic-swss-common.armhf/libswsscommon_1.0.0_armhf.deb - sudo dpkg -i sonic-swss-common.armhf/libswsscommon-dev_1.0.0_armhf.deb - sudo dpkg -i sonic-sairedis.armhf/libsaivs_*.deb - sudo dpkg -i sonic-sairedis.armhf/libsaivs-dev_*.deb - sudo dpkg -i sonic-sairedis.armhf/libsairedis_*.deb - sudo dpkg -i sonic-sairedis.armhf/libsairedis-dev_*.deb - sudo dpkg -i sonic-sairedis.armhf/libsaimetadata_*.deb - sudo dpkg -i sonic-sairedis.armhf/libsaimetadata-dev_*.deb - sudo dpkg -i sonic-sairedis.armhf/syncd-vs_*.deb - workingDirectory: $(Pipeline.Workspace) - displayName: "Install sonic swss common and sairedis" - - checkout: self - submodules: true - - script: | - ./autogen.sh - dpkg-buildpackage -us -uc -b -j$(nproc) && cp ../*.deb . - displayName: "Compile sonic swss" - - publish: $(System.DefaultWorkingDirectory)/ - artifact: sonic-swss.armhf - displayName: "Archive swss debian packages" +stages: +- stage: Build + + jobs: + - template: .azure-pipelines/build-template.yml + parameters: + arch: amd64 + sonic_slave: sonic-slave-buster + swss_common_artifact_name: sonic-swss-common + sairedis_artifact_name: sonic-sairedis + artifact_name: sonic-swss + + - template: .azure-pipelines/build-template.yml + parameters: + arch: armhf + timeout: 240 + pool: sonicbld + sonic_slave: sonic-slave-buster-armhf + swss_common_artifact_name: sonic-swss-common.armhf + sairedis_artifact_name: sonic-sairedis.armhf + artifact_name: sonic-swss.armhf + + - template: .azure-pipelines/build-template.yml + parameters: + arch: arm64 + timeout: 240 + pool: sonicbld + sonic_slave: sonic-slave-buster-arm64 + swss_common_artifact_name: sonic-swss-common.arm64 + sairedis_artifact_name: sonic-sairedis.arm64 + artifact_name: sonic-swss.arm64 From 3f11a5042b4a54730ff1d23f72531534d53b5e18 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Thu, 11 Feb 2021 12:16:44 -0800 Subject: [PATCH 11/54] [Mux] Neighbor handling based on FDB entry (#1631) * Mux neighbor handling based on FDB entry --- orchagent/muxorch.cpp | 188 +++++++++++++++++++++++++++++++++++---- orchagent/muxorch.h | 16 ++-- orchagent/neighorch.cpp | 5 +- orchagent/orchdaemon.cpp | 2 +- tests/test_mux.py | 2 + 5 files changed, 189 insertions(+), 24 deletions(-) diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 124a1cda7c..61ee046f19 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -19,6 +19,7 @@ #include "portsorch.h" #include "aclorch.h" #include "routeorch.h" +#include "fdborch.h" /* Global variables */ extern Directory gDirectory; @@ -27,6 +28,7 @@ extern NeighOrch *gNeighOrch; extern RouteOrch *gRouteOrch; extern AclOrch *gAclOrch; extern PortsOrch *gPortsOrch; +extern FdbOrch *gFdbOrch; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gUnderlayIfId; @@ -196,6 +198,10 @@ static sai_object_id_t create_tunnel(const IpAddress* p_dst_ip, const IpAddress* attr.value.s32 = SAI_TUNNEL_PEER_MODE_P2P; tunnel_attrs.push_back(attr); + attr.id = SAI_TUNNEL_ATTR_ENCAP_TTL_MODE; + attr.value.s32 = SAI_TUNNEL_TTL_MODE_PIPE_MODEL; + tunnel_attrs.push_back(attr); + if (p_src_ip != nullptr) { attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; @@ -474,7 +480,7 @@ void MuxCable::updateNeighbor(NextHopKey nh, bool add) { mux_orch_->addNexthop(nh, mux_name_); } - else + else if (mux_name_ == mux_orch_->getNexthopMuxName(nh)) { mux_orch_->removeNexthop(nh); } @@ -507,9 +513,11 @@ void MuxNbrHandler::update(NextHopKey nh, sai_object_id_t tunnelId, bool add, Mu break; case MuxState::MUX_STATE_ACTIVE: neighbors_[nh.ip_address] = gNeighOrch->getLocalNextHopId(nh); + gNeighOrch->enableNeighbor(nh); break; case MuxState::MUX_STATE_STANDBY: neighbors_[nh.ip_address] = tunnelId; + gNeighOrch->disableNeighbor(nh); mux_cb_orch->addTunnelRoute(nh); create_route(pfx, tunnelId); break; @@ -806,7 +814,6 @@ sai_object_id_t MuxOrch::getNextHopTunnelId(std::string tunnelKey, IpAddress& ip return it->second.nh_id; } - MuxCable* MuxOrch::findMuxCableInSubnet(IpAddress ip) { for (auto it = mux_cable_tb_.begin(); it != mux_cable_tb_.end(); it++) @@ -821,8 +828,13 @@ MuxCable* MuxOrch::findMuxCableInSubnet(IpAddress ip) return nullptr; } -bool MuxOrch::isNeighborActive(IpAddress nbr, string alias) +bool MuxOrch::isNeighborActive(const IpAddress& nbr, const MacAddress& mac, string& alias) { + if (mux_cable_tb_.empty()) + { + return true; + } + MuxCable* ptr = findMuxCableInSubnet(nbr); if (ptr) @@ -830,19 +842,145 @@ bool MuxOrch::isNeighborActive(IpAddress nbr, string alias) return ptr->isActive(); } + string port; + if (!getMuxPort(mac, alias, port)) + { + SWSS_LOG_INFO("Mux get port from FDB failed for '%s' mac '%s'", + nbr.to_string().c_str(), mac.to_string().c_str()); + return true; + } + + if (!port.empty() && isMuxExists(port)) + { + MuxCable* ptr = getMuxCable(port); + return ptr->isActive(); + } + + return true; +} + +bool MuxOrch::getMuxPort(const MacAddress& mac, const string& alias, string& portName) +{ + portName = std::string(); + Port rif, port; + + if (!gPortsOrch->getPort(alias, rif)) + { + SWSS_LOG_ERROR("Interface '%s' not found in port table", alias.c_str()); + return false; + } + + if (rif.m_type != Port::VLAN) + { + SWSS_LOG_DEBUG("Interface type for '%s' is not Vlan, type %d", alias.c_str(), rif.m_type); + return false; + } + + if (!gFdbOrch->getPort(mac, rif.m_vlan_info.vlan_id, port)) + { + SWSS_LOG_INFO("FDB entry not found: Vlan %s, mac %s", alias.c_str(), mac.to_string().c_str()); + return true; + } + + portName = port.m_alias; return true; } +void MuxOrch::updateFdb(const FdbUpdate& update) +{ + if (!update.add) + { + /* + * For Mac aging, flush events, skip updating mux neighbors. + * Instead, wait for neighbor update events + */ + return; + } + + NeighborEntry neigh; + MacAddress mac; + MuxCable* ptr; + for (auto nh = mux_nexthop_tb_.begin(); nh != mux_nexthop_tb_.end(); ++nh) + { + auto res = neigh_orch_->getNeighborEntry(nh->first, neigh, mac); + if (!res || update.entry.mac != mac) + { + continue; + } + + if (nh->second != update.entry.port_name) + { + if (!nh->second.empty() && isMuxExists(nh->second)) + { + ptr = getMuxCable(nh->second); + if (ptr->isIpInSubnet(nh->first.ip_address)) + { + continue; + } + nh->second = update.entry.port_name; + ptr->updateNeighbor(nh->first, false); + } + + if (isMuxExists(update.entry.port_name)) + { + ptr = getMuxCable(update.entry.port_name); + ptr->updateNeighbor(nh->first, true); + } + } + } +} + void MuxOrch::updateNeighbor(const NeighborUpdate& update) { + if (mux_cable_tb_.empty()) + { + return; + } + for (auto it = mux_cable_tb_.begin(); it != mux_cable_tb_.end(); it++) { MuxCable* ptr = it->second.get(); if (ptr->isIpInSubnet(update.entry.ip_address)) { ptr->updateNeighbor(update.entry, update.add); + return; + } + } + + string port, old_port; + if (update.add && !getMuxPort(update.mac, update.entry.alias, port)) + { + return; + } + else if (update.add) + { + /* Check if the neighbor already exists */ + old_port = getNexthopMuxName(update.entry); + addNexthop(update.entry); + } + else + { + auto it = mux_nexthop_tb_.find(update.entry); + if (it != mux_nexthop_tb_.end()) + { + port = it->second; + removeNexthop(update.entry); } } + + MuxCable* ptr; + if (!old_port.empty() && old_port != port && isMuxExists(old_port)) + { + ptr = getMuxCable(old_port); + ptr->updateNeighbor(update.entry, false); + addNexthop(update.entry); + } + + if (!port.empty() && isMuxExists(port)) + { + ptr = getMuxCable(port); + ptr->updateNeighbor(update.entry, update.add); + } } void MuxOrch::addNexthop(NextHopKey nh, string muxName) @@ -855,6 +993,16 @@ void MuxOrch::removeNexthop(NextHopKey nh) mux_nexthop_tb_.erase(nh); } +string MuxOrch::getNexthopMuxName(NextHopKey nh) +{ + if (mux_nexthop_tb_.find(nh) == mux_nexthop_tb_.end()) + { + return std::string(); + } + + return mux_nexthop_tb_[nh]; +} + sai_object_id_t MuxOrch::getNextHopId(const NextHopKey &nh) { if (mux_nexthop_tb_.find(nh) == mux_nexthop_tb_.end()) @@ -865,7 +1013,8 @@ sai_object_id_t MuxOrch::getNextHopId(const NextHopKey &nh) auto mux_name = mux_nexthop_tb_[nh]; if (!isMuxExists(mux_name)) { - SWSS_LOG_WARN("Mux entry for port '%s' doesn't exist", mux_name.c_str()); + SWSS_LOG_INFO("Mux entry for nh '%s' port '%s' doesn't exist", + nh.ip_address.to_string().c_str(), mux_name.c_str()); return SAI_NULL_OBJECT_ID; } @@ -888,6 +1037,12 @@ void MuxOrch::update(SubjectType type, void *cntx) updateNeighbor(*update); break; } + case SUBJECT_TYPE_FDB_CHANGE: + { + FdbUpdate *update = static_cast(cntx); + updateFdb(*update); + break; + } default: /* Received update in which we are not interested * Ignore it @@ -896,15 +1051,18 @@ void MuxOrch::update(SubjectType type, void *cntx) } } -MuxOrch::MuxOrch(DBConnector *db, const std::vector &tables, TunnelDecapOrch* decapOrch, NeighOrch* neighOrch) : +MuxOrch::MuxOrch(DBConnector *db, const std::vector &tables, + TunnelDecapOrch* decapOrch, NeighOrch* neighOrch, FdbOrch* fdbOrch) : Orch2(db, tables, request_), decap_orch_(decapOrch), - neigh_orch_(neighOrch) + neigh_orch_(neighOrch), + fdb_orch_(fdbOrch) { handler_map_.insert(handler_pair(CFG_MUX_CABLE_TABLE_NAME, &MuxOrch::handleMuxCfg)); handler_map_.insert(handler_pair(CFG_PEER_SWITCH_TABLE_NAME, &MuxOrch::handlePeerSwitch)); neigh_orch_->attach(this); + fdb_orch_->attach(this); } bool MuxOrch::handleMuxCfg(const Request& request) @@ -921,13 +1079,13 @@ bool MuxOrch::handleMuxCfg(const Request& request) { if(isMuxExists(port_name)) { - SWSS_LOG_ERROR("Mux for port '%s' already exists", port_name.c_str()); + SWSS_LOG_INFO("Mux for port '%s' already exists", port_name.c_str()); return true; } if (mux_peer_switch_.isZero()) { - SWSS_LOG_ERROR("Peer switch addr not yet configured, port '%s'", port_name.c_str()); + SWSS_LOG_INFO("Mux Peer switch addr not yet configured, port '%s'", port_name.c_str()); return false; } @@ -998,7 +1156,7 @@ bool MuxOrch::addOperation(const Request& request) auto& tn = request.getTableName(); if (handler_map_.find(tn) == handler_map_.end()) { - SWSS_LOG_ERROR(" %s handler is not initialized", tn.c_str()); + SWSS_LOG_ERROR("Mux %s handler is not initialized", tn.c_str()); return true; } @@ -1022,7 +1180,7 @@ bool MuxOrch::delOperation(const Request& request) auto& tn = request.getTableName(); if (handler_map_.find(tn) == handler_map_.end()) { - SWSS_LOG_ERROR(" %s handler is not initialized", tn.c_str()); + SWSS_LOG_ERROR("Mux %s handler is not initialized", tn.c_str()); return true; } @@ -1099,7 +1257,7 @@ bool MuxCableOrch::addOperation(const Request& request) MuxOrch* mux_orch = gDirectory.get(); if (!mux_orch->isMuxExists(port_name)) { - SWSS_LOG_WARN("Mux entry for port '%s' doesn't exist", port_name.c_str()); + SWSS_LOG_INFO("Mux entry for port '%s' doesn't exist", port_name.c_str()); return false; } @@ -1112,7 +1270,7 @@ bool MuxCableOrch::addOperation(const Request& request) } catch(const std::runtime_error& error) { - SWSS_LOG_ERROR("Error setting state %s for port %s. Error: %s", + SWSS_LOG_ERROR("Mux Error setting state %s for port %s. Error: %s", state.c_str(), port_name.c_str(), error.what()); return false; } @@ -1171,13 +1329,13 @@ bool MuxStateOrch::addOperation(const Request& request) } catch(const std::runtime_error& error) { - SWSS_LOG_ERROR("Error getting state for port %s Error: %s", port_name.c_str(), error.what()); + SWSS_LOG_ERROR("Mux error getting state for port %s Error: %s", port_name.c_str(), error.what()); return false; } if (mux_obj->isStateChangeInProgress()) { - SWSS_LOG_NOTICE("Mux state change for port '%s' is in-progress", port_name.c_str()); + SWSS_LOG_INFO("Mux state change for port '%s' is in-progress", port_name.c_str()); return false; } @@ -1186,7 +1344,7 @@ bool MuxStateOrch::addOperation(const Request& request) mux_state = MUX_HW_STATE_UNKNOWN; } - SWSS_LOG_NOTICE("Setting State DB entry (hw state %s, mux state %s) for port %s", + SWSS_LOG_NOTICE("Mux setting State DB entry (hw state %s, mux state %s) for port %s", hw_state.c_str(), mux_state.c_str(), port_name.c_str()); updateMuxState(port_name, mux_state); diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index 2e97c5b609..f52343f22c 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -152,7 +152,7 @@ class MuxCfgRequest : public Request class MuxOrch : public Orch2, public Observer, public Subject { public: - MuxOrch(DBConnector *db, const std::vector &tables, TunnelDecapOrch*, NeighOrch*); + MuxOrch(DBConnector *db, const std::vector &tables, TunnelDecapOrch*, NeighOrch*, FdbOrch*); using handler_pair = pair; using handler_map = map; @@ -168,12 +168,12 @@ class MuxOrch : public Orch2, public Observer, public Subject } MuxCable* findMuxCableInSubnet(IpAddress); - bool isNeighborActive(IpAddress nbr, string alias); + bool isNeighborActive(const IpAddress&, const MacAddress&, string&); void update(SubjectType, void *); - void updateNeighbor(const NeighborUpdate&); - void addNexthop(NextHopKey, string); + void addNexthop(NextHopKey, string = ""); void removeNexthop(NextHopKey); + string getNexthopMuxName(NextHopKey); sai_object_id_t getNextHopId(const NextHopKey&); sai_object_id_t createNextHopTunnel(std::string tunnelKey, IpAddress& ipAddr); @@ -187,8 +187,13 @@ class MuxOrch : public Orch2, public Observer, public Subject bool handleMuxCfg(const Request&); bool handlePeerSwitch(const Request&); + void updateNeighbor(const NeighborUpdate&); + void updateFdb(const FdbUpdate&); + + bool getMuxPort(const MacAddress&, const string&, string&); + IpAddress mux_peer_switch_ = 0x0; - sai_object_id_t mux_tunnel_id_; + sai_object_id_t mux_tunnel_id_ = SAI_NULL_OBJECT_ID; MuxCableTb mux_cable_tb_; MuxTunnelNHs mux_tunnel_nh_; @@ -198,6 +203,7 @@ class MuxOrch : public Orch2, public Observer, public Subject TunnelDecapOrch *decap_orch_; NeighOrch *neigh_orch_; + FdbOrch *fdb_orch_; MuxCfgRequest request_; }; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 267e51544a..ef10d8764d 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -653,9 +653,8 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress MuxOrch* mux_orch = gDirectory.get(); bool hw_config = isHwConfigured(neighborEntry); - if (!hw_config && mux_orch->isNeighborActive(ip_address, alias)) + if (!hw_config && mux_orch->isNeighborActive(ip_address, macAddress, alias)) { - if (gMySwitchType == "voq") { if (!addVoqEncapIndex(alias, ip_address, neighbor_attrs)) @@ -769,7 +768,7 @@ bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry, bool disable) return true; } - if (m_syncdNextHops[nexthop].ref_count > 0) + if (m_syncdNextHops.find(nexthop) != m_syncdNextHops.end() && m_syncdNextHops[nexthop].ref_count > 0) { SWSS_LOG_INFO("Failed to remove still referenced neighbor %s on %s", m_syncdNeighbors[neighborEntry].mac.to_string().c_str(), alias.c_str()); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 638115f631..c2246be280 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -253,7 +253,7 @@ bool OrchDaemon::init() CFG_MUX_CABLE_TABLE_NAME, CFG_PEER_SWITCH_TABLE_NAME }; - MuxOrch *mux_orch = new MuxOrch(m_configDb, mux_tables, tunnel_decap_orch, gNeighOrch); + MuxOrch *mux_orch = new MuxOrch(m_configDb, mux_tables, tunnel_decap_orch, gNeighOrch, gFdbOrch); gDirectory.set(mux_orch); MuxCableOrch *mux_cb_orch = new MuxCableOrch(m_applDb, APP_MUX_CABLE_TABLE_NAME); diff --git a/tests/test_mux.py b/tests/test_mux.py index c0a0aa1db8..659c686e19 100644 --- a/tests/test_mux.py +++ b/tests/test_mux.py @@ -104,6 +104,8 @@ def create_and_test_peer(self, db, asicdb, peer_name, peer_ip, src_ip): assert self.check_interface_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE": assert self.check_interface_exists_in_asicdb(asicdb, value) + elif field == "SAI_TUNNEL_ATTR_ENCAP_TTL_MODE": + assert value == "SAI_TUNNEL_TTL_MODE_PIPE_MODEL" else: assert False, "Field %s is not tested" % field From 0dea91cfefeaa253ba4814e2a4da7a4453ad5a4a Mon Sep 17 00:00:00 2001 From: Guohan Lu Date: Fri, 12 Feb 2021 01:41:12 -0800 Subject: [PATCH 12/54] [test_virtual_chassis]: use wait_for function to improve test robustness Signed-off-by: Guohan Lu --- tests/test_virtual_chassis.py | 67 ++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/tests/test_virtual_chassis.py b/tests/test_virtual_chassis.py index 4bdbc6d397..14732e6b99 100644 --- a/tests/test_virtual_chassis.py +++ b/tests/test_virtual_chassis.py @@ -30,10 +30,10 @@ def test_connectivity(self, vct): def test_voq_switch(self, vct): """Test VOQ switch objects configuration. - + This test validates configuration of switch creation objects required for VOQ switches. The switch_type, max_cores and switch_id attributes configuration - are verified. For the System port config list, it is verified that all the + are verified. For the System port config list, it is verified that all the configured system ports are avaiable in the asic db by checking the count. """ @@ -54,40 +54,40 @@ def test_voq_switch(self, vct): cfg_max_cores = metatbl.get("max_cores") assert cfg_max_cores != "", "Got error in getting max_cores from CONFIG_DB DEVICE_METADATA" - + cfgspkeys = config_db.get_keys("SYSTEM_PORT") sp_count = len(cfgspkeys) asic_db = dvs.get_asic_db() keys = asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") switch_oid_key = keys[0] - + switch_entry = asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_SWITCH", switch_oid_key) value = switch_entry.get("SAI_SWITCH_ATTR_TYPE") assert value == "SAI_SWITCH_TYPE_VOQ", "Switch type is not VOQ" - + value = switch_entry.get("SAI_SWITCH_ATTR_SWITCH_ID") assert value == cfg_switch_id, "VOQ switch id is invalid" - + value = switch_entry.get("SAI_SWITCH_ATTR_MAX_SYSTEM_CORES") assert value == cfg_max_cores, "Max system cores is invalid" - + value = switch_entry.get("SAI_SWITCH_ATTR_SYSTEM_PORT_CONFIG_LIST") assert value != "", "Empty system port config list" # Convert the spcfg string to dictionary spcfg = ast.literal_eval(value) assert spcfg['count'] == sp_count, "Number of systems ports configured is invalid" - + def test_chassis_app_db_sync(self, vct): """Test chassis app db syncing. - + This test is for verifying the database sync mechanism. With the virtual chassis setup, it is verified that at least one database entry is synced from line card to supervisor card. An interface entry is used as sample database entry for verification of syncing mechanism. """ - + dvss = vct.dvss for name in dvss.keys(): if name.startswith("supervisor"): @@ -95,17 +95,17 @@ def test_chassis_app_db_sync(self, vct): chassis_app_db = DVSDatabase(swsscommon.CHASSIS_APP_DB, dvs.redis_chassis_sock) keys = chassis_app_db.get_keys("SYSTEM_INTERFACE") assert len(keys), "No chassis app db syncing is done" - + def test_chassis_system_interface(self, vct): """Test RIF record creation in ASIC_DB for remote interfaces. - + This test verifies RIF programming in ASIC_DB for remote interface. The orchagent creates RIF record for system port interfaces from other line cards. It is verified by retrieving a RIF record from local ASIC_DB that corresponds to a remote system port - and checking that the switch id of that remote system port does not match the local asic + and checking that the switch id of that remote system port does not match the local asic switch id. """ - + dvss = vct.dvss for name in dvss.keys(): dvs = dvss[name] @@ -116,11 +116,11 @@ def test_chassis_system_interface(self, vct): cfg_switch_type = metatbl.get("switch_type") # Test only for line cards - if cfg_switch_type == "voq": + if cfg_switch_type == "voq": lc_switch_id = metatbl.get("switch_id") assert lc_switch_id != "", "Got error in getting switch_id from CONFIG_DB DEVICE_METADATA" if lc_switch_id == "0": - # Testing in Linecard1, In Linecard1 there will be RIF for Ethernet12 from Linecard3 + # Testing in Linecard1, In Linecard1 there will be RIF for Ethernet12 from Linecard3 # Note: Tesing can be done in any linecard for RIF of any system port interface. # Here testing is done on linecard with switch id 0 asic_db = dvs.get_asic_db() @@ -151,7 +151,7 @@ def test_chassis_system_interface(self, vct): def test_chassis_system_neigh(self, vct): """Test neigh record creation and syncing to chassis app db. - + This test validates that: (i) Local neighbor entry is created with encap index (ii) Local neighbor is synced to chassis ap db with assigned encap index @@ -159,6 +159,7 @@ def test_chassis_system_neigh(self, vct): """ dvss = vct.dvss + print("name {}".format(dvss.keys())) for name in dvss.keys(): dvs = dvss[name] @@ -168,19 +169,20 @@ def test_chassis_system_neigh(self, vct): cfg_switch_type = metatbl.get("switch_type") # Neighbor record verifiation done in line card - if cfg_switch_type == "voq": + if cfg_switch_type == "voq": lc_switch_id = metatbl.get("switch_id") assert lc_switch_id != "", "Got error in getting switch_id from CONFIG_DB DEVICE_METADATA" if lc_switch_id == "0": # Add a static neighbor + _, res = dvs.runcmd(['sh', "-c", "ip neigh show"]) _, res = dvs.runcmd(['sh', "-c", "ip neigh add 10.8.101.2 lladdr 00:01:02:03:04:05 dev Ethernet0"]) assert res == "", "Error configuring static neigh" asic_db = dvs.get_asic_db() neighkeys = asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") assert len(neighkeys), "No neigh entries in ASIC_DB" - + # Check for presence of the neighbor in ASIC_DB test_neigh = "" for nkey in neighkeys: @@ -188,25 +190,26 @@ def test_chassis_system_neigh(self, vct): if ne['ip'] == '10.8.101.2': test_neigh = nkey break - + assert test_neigh != "", "Neigh not found in ASIC_DB" - + # Check for presence of encap index, retrieve and store it for sync verification - test_neigh_entry = asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", test_neigh) + test_neigh_entry = asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", test_neigh) encap_index = test_neigh_entry.get("SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX") assert encap_index != "", "VOQ encap index is not programmed in ASIC_DB" - + break - - # Verify neighbor record syncing with encap index + + # Verify neighbor record syncing with encap index dvss = vct.dvss for name in dvss.keys(): if name.startswith("supervisor"): dvs = dvss[name] chassis_app_db = DVSDatabase(swsscommon.CHASSIS_APP_DB, dvs.redis_chassis_sock) + chassis_app_db.wait_for_n_keys("SYSTEM_NEIGH", 1) sysneighkeys = chassis_app_db.get_keys("SYSTEM_NEIGH") - assert len(sysneighkeys), "No system neighbor entries in chassis app db" - + + print(sysneighkeys) test_sysneigh = "" for sysnk in sysneighkeys: sysnk_tok = sysnk.split("|") @@ -214,15 +217,15 @@ def test_chassis_system_neigh(self, vct): if sysnk_tok[2] == "10.8.101.2": test_sysneigh = sysnk break - + assert test_sysneigh != "", "Neigh is not sync-ed to chassis app db" - - test_sysneigh_entry = chassis_app_db.get_entry("SYSTEM_NEIGH", test_sysneigh) + + test_sysneigh_entry = chassis_app_db.get_entry("SYSTEM_NEIGH", test_sysneigh) sys_neigh_encap_index = test_sysneigh_entry.get("encap_index") assert sys_neigh_encap_index != "", "System neigh in chassis app db does not have encap index" - + assert encap_index == sys_neigh_encap_index, "Encap index not sync-ed correctly" - + break # Add Dummy always-pass test at end as workaroud From f9d5959eb6cbbf467ee9844555319b729994d941 Mon Sep 17 00:00:00 2001 From: Guohan Lu Date: Thu, 11 Feb 2021 02:01:23 -0800 Subject: [PATCH 13/54] [ci]: run vstest Signed-off-by: Guohan Lu --- .../build-docker-sonic-vs-template.yml | 85 ++++++++++++++++++ .azure-pipelines/build-template.yml | 1 + .azure-pipelines/build_and_install_module.sh | 87 +++++++++++++++++++ .azure-pipelines/docker-sonic-vs/Dockerfile | 16 ++++ .../test-docker-sonic-vs-template.yml | 70 +++++++++++++++ azure-pipelines.yml | 23 +++++ 6 files changed, 282 insertions(+) create mode 100644 .azure-pipelines/build-docker-sonic-vs-template.yml create mode 100755 .azure-pipelines/build_and_install_module.sh create mode 100644 .azure-pipelines/docker-sonic-vs/Dockerfile create mode 100644 .azure-pipelines/test-docker-sonic-vs-template.yml diff --git a/.azure-pipelines/build-docker-sonic-vs-template.yml b/.azure-pipelines/build-docker-sonic-vs-template.yml new file mode 100644 index 0000000000..97e8afb394 --- /dev/null +++ b/.azure-pipelines/build-docker-sonic-vs-template.yml @@ -0,0 +1,85 @@ +parameters: +- name: arch + type: string + values: + - amd64 + - armhf + - arm64 + default: amd64 + +- name: timeout + type: number + default: 60 + +- name: swss_artifact_name + type: string + +- name: sairedis_artifact_name + type: string + +- name: swss_common_artifact_name + type: string + +- name: artifact_name + type: string + +jobs: +- job: + displayName: ${{ parameters.arch }} + timeoutInMinutes: ${{ parameters.timeout }} + + pool: + vmImage: 'ubuntu-20.04' + + steps: + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 9 + artifact: ${{ parameters.swss_common_artifact_name }} + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic swss common deb packages" + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 12 + artifact: ${{ parameters.sairedis_artifact_name }} + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic sairedis deb packages" + - task: DownloadPipelineArtifact@2 + inputs: + artifact: ${{ parameters.swss_artifact_name }} + displayName: "Download sonic swss artifact" + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 1 + artifact: sonic-buildimage.vs + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic buildimage" + - script: | + echo $(Build.DefinitionName).$(Build.BuildNumber) + + docker load < ../target/docker-sonic-vs.gz + + mkdir -p .azure-pipelines/docker-sonic-vs/debs + + cp -v ../*.deb .azure-pipelines/docker-sonic-vs/debs + + pushd .azure-pipelines + + docker build --no-cache -t docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber) docker-sonic-vs + + popd + + docker save docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber) | gzip -c > $(Build.ArtifactStagingDirectory)/docker-sonic-vs.gz + + - publish: $(Build.ArtifactStagingDirectory)/ + artifact: ${{ parameters.artifact_name }} + displayName: "Archive sonic docker vs image" diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index 7d8e12c3ab..7cc6350e2f 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -5,6 +5,7 @@ parameters: - amd64 - armhf - arm64 + default: amd64 - name: pool type: string diff --git a/.azure-pipelines/build_and_install_module.sh b/.azure-pipelines/build_and_install_module.sh new file mode 100755 index 0000000000..4bd026cb52 --- /dev/null +++ b/.azure-pipelines/build_and_install_module.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# +# build and install team/vrf driver +# + +set -e + +source /etc/os-release + +function build_and_install_kmodule() +{ + if sudo modprobe team 2>/dev/null && sudo modprobe vrf 2>/dev/null && sudo modprobe macsec 2>/dev/null; then + echo "The module team, vrf and macsec exist." + return + fi + + [ -z "$WORKDIR" ] && WORKDIR=$(mktemp -d) + cd $WORKDIR + + KERNEL_RELEASE=$(uname -r) + KERNEL_MAINVERSION=$(echo $KERNEL_RELEASE | cut -d- -f1) + EXTRAVERSION=$(echo $KERNEL_RELEASE | cut -d- -f2) + LOCALVERSION=$(echo $KERNEL_RELEASE | cut -d- -f3) + VERSION=$(echo $KERNEL_MAINVERSION | cut -d. -f1) + PATCHLEVEL=$(echo $KERNEL_MAINVERSION | cut -d. -f2) + SUBLEVEL=$(echo $KERNEL_MAINVERSION | cut -d. -f3) + + # Install the required debian packages to build the kernel modules + apt-get install -y build-essential linux-headers-${KERNEL_RELEASE} autoconf pkg-config fakeroot + apt-get install -y flex bison libssl-dev libelf-dev + apt-get install -y libnl-route-3-200 libnl-route-3-dev libnl-cli-3-200 libnl-cli-3-dev libnl-3-dev + + # Add the apt source mirrors and download the linux image source code + cp /etc/apt/sources.list /etc/apt/sources.list.bk + sed -i "s/^# deb-src/deb-src/g" /etc/apt/sources.list + apt-get update + apt-get source linux-image-unsigned-$(uname -r) > source.log + + # Recover the original apt sources list + cp /etc/apt/sources.list.bk /etc/apt/sources.list + apt-get update + + # Build the Linux kernel module drivers/net/team and vrf + cd $(find . -maxdepth 1 -type d | grep -v "^.$") + make allmodconfig + mv .config .config.bk + cp /boot/config-$(uname -r) .config + grep NET_TEAM .config.bk >> .config + echo CONFIG_NET_VRF=m >> .config + echo CONFIG_MACSEC=m >> .config + make VERSION=$VERSION PATCHLEVEL=$PATCHLEVEL SUBLEVEL=$SUBLEVEL EXTRAVERSION=-${EXTRAVERSION} LOCALVERSION=-${LOCALVERSION} modules_prepare + make M=drivers/net/team + mv drivers/net/Makefile drivers/net/Makefile.bak + echo 'obj-$(CONFIG_NET_VRF) += vrf.o' > drivers/net/Makefile + echo 'obj-$(CONFIG_MACSEC) += macsec.o' >> drivers/net/Makefile + make M=drivers/net + + # Install the module + TEAM_DIR=$(echo /lib/modules/$(uname -r)/kernel/net/team) + NET_DIR=$(echo /lib/modules/$(uname -r)/kernel/net) + if [ ! -e "$TEAM_DIR/team.ko" ]; then + mkdir -p $TEAM_DIR + cp drivers/net/team/*.ko $TEAM_DIR/ + modinfo $TEAM_DIR/team.ko + depmod + modprobe team + fi + if [ ! -e "$NET_DIR/vrf.ko" ]; then + mkdir -p $NET_DIR + cp drivers/net/vrf.ko $NET_DIR/ + modinfo $NET_DIR/vrf.ko + depmod + modprobe vrf + fi + if [ ! -e "$NET_DIR/macsec.ko" ]; then + mkdir -p $NET_DIR + cp drivers/net/macsec.ko $NET_DIR/ + modinfo $NET_DIR/macsec.ko + depmod + modprobe macsec + fi + + cd /tmp + rm -rf $WORKDIR +} + +build_and_install_kmodule diff --git a/.azure-pipelines/docker-sonic-vs/Dockerfile b/.azure-pipelines/docker-sonic-vs/Dockerfile new file mode 100644 index 0000000000..f2bfb425b9 --- /dev/null +++ b/.azure-pipelines/docker-sonic-vs/Dockerfile @@ -0,0 +1,16 @@ +FROM docker-sonic-vs + +ARG docker_container_name + +ADD ["debs", "/debs"] + +RUN dpkg -i /debs/libswsscommon_1.0.0_amd64.deb +RUN dpkg -i /debs/python-swsscommon_1.0.0_amd64.deb +RUN dpkg -i /debs/python3-swsscommon_1.0.0_amd64.deb + +RUN dpkg -i /debs/libsaimetadata_1.0.0_amd64.deb +RUN dpkg -i /debs/libsairedis_1.0.0_amd64.deb +RUN dpkg -i /debs/libsaivs_1.0.0_amd64.deb +RUN dpkg -i /debs/syncd-vs_1.0.0_amd64.deb + +RUN dpkg -i /debs/swss_1.0.0_amd64.deb diff --git a/.azure-pipelines/test-docker-sonic-vs-template.yml b/.azure-pipelines/test-docker-sonic-vs-template.yml new file mode 100644 index 0000000000..a91f6ab59c --- /dev/null +++ b/.azure-pipelines/test-docker-sonic-vs-template.yml @@ -0,0 +1,70 @@ +parameters: +- name: timeout + type: number + default: 180 + +- name: log_artifact_name + type: string + +jobs: +- job: + displayName: vstest + timeoutInMinutes: ${{ parameters.timeout }} + + pool: + vmImage: 'ubuntu-20.04' + + steps: + - task: DownloadPipelineArtifact@2 + inputs: + artifact: docker-sonic-vs + displayName: "Download docker sonic vs image" + + - task: DownloadPipelineArtifact@2 + inputs: + source: specific + project: build + pipeline: 9 + artifact: sonic-swss-common.amd64.ubuntu20_04 + runVersion: 'latestFromBranch' + runBranch: 'refs/heads/master' + displayName: "Download sonic swss common deb packages" + + - script: | + set -x + sudo .azure-pipelines/build_and_install_module.sh + + sudo apt-get install -y libhiredis0.14 + sudo dpkg -i --force-confask,confnew ../libswsscommon_1.0.0_amd64.deb || apt-get install -f + sudo dpkg -i ../python3-swsscommon_1.0.0_amd64.deb + + # install packages for vs test + sudo apt-get install -y net-tools bridge-utils vlan + sudo apt-get install -y python3-pip + sudo pip3 install pytest==4.6.2 attrs==19.1.0 exabgp==4.0.10 distro==1.5.0 docker==4.4.1 redis==3.3.4 flaky==3.7.0 + displayName: "Install dependencies" + + - script: | + set -x + sudo docker load -i ../docker-sonic-vs.gz + docker ps + ip netns list + pushd tests + sudo py.test -v --force-flaky --junitxml=tr.xml --imgname=docker-sonic-vs:$(Build.DefinitionName).$(Build.BuildNumber) + displayName: "Run vs tests" + + - task: PublishTestResults@2 + inputs: + testResultsFiles: '**/tr.xml' + testRunTitle: vstest + condition: always() + + - script: | + cp -r tests/log $(Build.ArtifactStagingDirectory)/ + displayName: "Collect logs" + condition: always() + + - publish: $(Build.ArtifactStagingDirectory)/ + artifact: ${{ parameters.log_artifact_name }}@$(System.JobAttempt) + displayName: "Publish logs" + condition: always() diff --git a/azure-pipelines.yml b/azure-pipelines.yml index af3222caec..4f2762d348 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,6 +20,10 @@ stages: sairedis_artifact_name: sonic-sairedis artifact_name: sonic-swss +- stage: BuildArm + dependsOn: Build + condition: succeeded('Build') + jobs: - template: .azure-pipelines/build-template.yml parameters: arch: armhf @@ -39,3 +43,22 @@ stages: swss_common_artifact_name: sonic-swss-common.arm64 sairedis_artifact_name: sonic-sairedis.arm64 artifact_name: sonic-swss.arm64 + +- stage: BuildDocker + dependsOn: Build + condition: succeeded('Build') + jobs: + - template: .azure-pipelines/build-docker-sonic-vs-template.yml + parameters: + swss_common_artifact_name: sonic-swss-common + sairedis_artifact_name: sonic-sairedis + swss_artifact_name: sonic-swss + artifact_name: docker-sonic-vs + +- stage: Test + dependsOn: BuildDocker + condition: succeeded('BuildDocker') + jobs: + - template: .azure-pipelines/test-docker-sonic-vs-template.yml + parameters: + log_artifact_name: log From 32bc1b6c148495fb8fca00a3ad78a635e50c3139 Mon Sep 17 00:00:00 2001 From: Neetha John Date: Fri, 12 Feb 2021 23:14:01 -0800 Subject: [PATCH 14/54] [bufferorch] Handle NOT IMPLEMENTED status returned during set attr operation (#1639) Set operations for some attributes of buffer pool and buffer profile may not be implemented by some vendors and they return SAI_STATUS_ATTR_NOT_IMPLEMENTED_0. Handle that status in the code and ignore that task Signed-off-by: Neetha John --- orchagent/bufferorch.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index c7977df432..c43ac25bd4 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -367,7 +367,12 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) for (auto &attribute : attribs) { sai_status = sai_buffer_api->set_buffer_pool_attribute(sai_object, &attribute); - if (SAI_STATUS_SUCCESS != sai_status) + if (SAI_STATUS_ATTR_NOT_IMPLEMENTED_0 == sai_status) + { + SWSS_LOG_NOTICE("Buffer pool SET for name:%s, sai object:%" PRIx64 ", not implemented. status:%d. Ignoring it", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_ignore; + } + else if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to modify buffer pool, name:%s, sai object:%" PRIx64 ", status:%d", object_name.c_str(), sai_object, sai_status); return task_process_status::task_failed; @@ -553,7 +558,12 @@ task_process_status BufferOrch::processBufferProfile(KeyOpFieldsValuesTuple &tup for (auto &attribute : attribs) { sai_status = sai_buffer_api->set_buffer_profile_attribute(sai_object, &attribute); - if (SAI_STATUS_SUCCESS != sai_status) + if (SAI_STATUS_ATTR_NOT_IMPLEMENTED_0 == sai_status) + { + SWSS_LOG_NOTICE("Buffer profile SET for name:%s, sai object:%" PRIx64 ", not implemented. status:%d. Ignoring it", object_name.c_str(), sai_object, sai_status); + return task_process_status::task_ignore; + } + else if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to modify buffer profile, name:%s, sai object:%" PRIx64 ", status:%d", object_name.c_str(), sai_object, sai_status); return task_process_status::task_failed; @@ -1011,6 +1021,7 @@ void BufferOrch::doTask(Consumer &consumer) switch(task_status) { case task_process_status::task_success : + case task_process_status::task_ignore : it = consumer.m_toSync.erase(it); break; case task_process_status::task_invalid_entry: From f2854f8b3a0e1971a4123e426cc483cb03d57ea8 Mon Sep 17 00:00:00 2001 From: liat-grozovik <44433539+liat-grozovik@users.noreply.github.com> Date: Sun, 14 Feb 2021 22:35:07 +0200 Subject: [PATCH 15/54] spell check fixes (#1630) --- cfgmgr/buffermgr.cpp | 2 +- cfgmgr/buffermgrdyn.cpp | 8 ++++---- cfgmgr/buffermgrdyn.h | 2 +- cfgmgr/intfmgr.cpp | 2 +- cfgmgr/natmgr.h | 2 +- cfgmgr/sflowmgr.cpp | 2 +- cfgmgr/vxlanmgr.cpp | 6 +++--- fdbsyncd/fdbsyncd.cpp | 2 +- fpmsyncd/fpmsyncd.cpp | 2 +- mclagsyncd/mclaglink.cpp | 4 ++-- mclagsyncd/mclagsyncd.cpp | 2 +- neighsyncd/neighsync.cpp | 2 +- neighsyncd/neighsyncd.cpp | 2 +- orchagent/aclorch.cpp | 6 +++--- orchagent/aclorch.h | 6 +++--- orchagent/bufferorch.cpp | 2 +- orchagent/crmorch.cpp | 2 +- orchagent/debugcounterorch.cpp | 8 ++++---- orchagent/fdborch.cpp | 4 ++-- orchagent/fgnhgorch.cpp | 18 +++++++++--------- orchagent/fgnhgorch.h | 2 +- orchagent/intfsorch.cpp | 2 +- orchagent/main.cpp | 6 +++--- orchagent/mirrororch.cpp | 10 +++++----- orchagent/natorch.cpp | 8 ++++---- orchagent/neighorch.cpp | 2 +- orchagent/orch.cpp | 6 +++--- orchagent/orchdaemon.cpp | 6 +++--- orchagent/pfcactionhandler.cpp | 2 +- orchagent/pfcactionhandler.h | 2 +- orchagent/pfcwdorch.cpp | 8 ++++---- orchagent/policerorch.cpp | 2 +- orchagent/qosorch.cpp | 4 ++-- orchagent/routeorch.cpp | 6 +++--- swssconfig/swssconfig.cpp | 2 +- teamsyncd/teamsyncd.cpp | 2 +- tests/mock_tests/aclorch_ut.cpp | 12 ++++++------ tests/mock_tests/portsorch_ut.cpp | 4 ++-- tests/mock_tests/saispy.h | 4 ++-- warmrestart/warmRestartAssist.cpp | 4 ++-- warmrestart/warmRestartAssist.h | 16 ++++++++-------- 41 files changed, 97 insertions(+), 97 deletions(-) diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index 4f7a94186c..7b1b73faff 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -246,7 +246,7 @@ void BufferMgr::transformReference(string &name) * This function copies the data from tables in CONFIG_DB to APPL_DB. * With dynamically buffer calculation supported, the following tables * will be moved to APPL_DB from CONFIG_DB because the CONFIG_DB contains - * confgured entries only while APPL_DB contains dynamically generated entries + * configured entries only while APPL_DB contains dynamically generated entries * - BUFFER_POOL * - BUFFER_PROFILE * - BUFFER_PG diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index bff7acfd7d..fb00a8a779 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -19,7 +19,7 @@ * 1. All keys in this file are in format of APPL_DB key. * Key population: * On receiving item update from CONFIG_DB: key has been transformed into the format of APPL_DB - * In intermal maps: table name removed from the index + * In internal maps: table name removed from the index * 2. Maintain maps for pools, profiles and PGs in CONFIG_DB and APPL_DB * 3. Keys of maps in this file don't contain the TABLE_NAME * 3. @@ -468,7 +468,7 @@ void BufferMgrDynamic::checkSharedBufferPoolSize() // Eventually, the correct values will pushed to APPL_DB and then ASIC_DB recalculateSharedBufferPool(); m_firstTimeCalculateBufferPool = false; - SWSS_LOG_NOTICE("Buffer pool update defered because port is still under initialization, start polling timer"); + SWSS_LOG_NOTICE("Buffer pool update deferred because port is still under initialization, start polling timer"); } return; @@ -712,7 +712,7 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ } //Called when speed/cable length updated from CONFIG_DB -// Update buffer profile of a certern PG of a port or all PGs of the port according to its speed, cable_length and mtu +// Update buffer profile of a certain PG of a port or all PGs of the port according to its speed, cable_length and mtu // Called when // - port's speed, cable_length or mtu updated // - one buffer pg of port's is set to dynamic calculation @@ -1814,7 +1814,7 @@ task_process_status BufferMgrDynamic::handleBufferPortEgressProfileListTable(Key * This function copies the data from tables in CONFIG_DB to APPL_DB. * With dynamically buffer calculation supported, the following tables * will be moved to APPL_DB from CONFIG_DB because the CONFIG_DB contains - * confgured entries only while APPL_DB contains dynamically generated entries + * configured entries only while APPL_DB contains dynamically generated entries * - BUFFER_POOL * - BUFFER_PROFILE * - BUFFER_PG diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index f11bd86201..a5ffe39b1e 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -81,7 +81,7 @@ typedef struct { typedef enum { // Port is under initializing, which means its info hasn't been comprehensive for calculating headroom PORT_INITIALIZING, - // All necessary information for calculating headrom is ready + // All necessary information for calculating headroom is ready PORT_READY } port_state_t; diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index c8cb075574..24516fff1a 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -613,7 +613,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, else if (op == DEL_COMMAND) { /* make sure all ip addresses associated with interface are removed, otherwise these ip address would - be set with global vrf and it may cause ip address confliction. */ + be set with global vrf and it may cause ip address conflict. */ if (getIntfIpCount(alias)) { return false; diff --git a/cfgmgr/natmgr.h b/cfgmgr/natmgr.h index aa7d159523..83453775a9 100644 --- a/cfgmgr/natmgr.h +++ b/cfgmgr/natmgr.h @@ -276,7 +276,7 @@ class NatMgr : public Orch natDnatPool_map_t m_natDnatPoolInfo; SelectableTimer *m_natRefreshTimer; - /* Declare doTask related fucntions */ + /* Declare doTask related functions */ void doTask(Consumer &consumer); void doTask(SelectableTimer &timer); void doNatRefreshTimerTask(); diff --git a/cfgmgr/sflowmgr.cpp b/cfgmgr/sflowmgr.cpp index 792d583ace..aefcffe8e8 100644 --- a/cfgmgr/sflowmgr.cpp +++ b/cfgmgr/sflowmgr.cpp @@ -247,7 +247,7 @@ void SflowMgr::sflowCheckAndFillValues(string alias, vector &va { if (m_sflowPortConfMap[alias].admin == "") { - /* By default admin state is enable if not set explicitely */ + /* By default admin state is enable if not set explicitly */ m_sflowPortConfMap[alias].admin = "up"; } FieldValueTuple fv("admin_state", m_sflowPortConfMap[alias].admin); diff --git a/cfgmgr/vxlanmgr.cpp b/cfgmgr/vxlanmgr.cpp index 9ad06d9adb..1d77efc2b8 100644 --- a/cfgmgr/vxlanmgr.cpp +++ b/cfgmgr/vxlanmgr.cpp @@ -906,7 +906,7 @@ void VxlanMgr::createAppDBTunnelMapTable(const KeyOpFieldsValuesTuple & t) std::replace(vxlanTunnelMapName.begin(), vxlanTunnelMapName.end(), config_db_key_delimiter, delimiter); /* Case 1: Entry exist - Erase from cache & return - * Case 2: Enry does not exist - Write to AppDB + * Case 2: Entry does not exist - Write to AppDB * Case 3: Entry exist but modified - Not taken care. Will address later */ if (m_in_reconcile) @@ -921,7 +921,7 @@ void VxlanMgr::createAppDBTunnelMapTable(const KeyOpFieldsValuesTuple & t) } else { - SWSS_LOG_INFO("Reconcile App Tunnel Map Table create %s doesnt not exist. Pending %zu", + SWSS_LOG_INFO("Reconcile App Tunnel Map Table create %s does not exist. Pending %zu", vxlanTunnelMapName.c_str(), m_appVxlanTunnelMapKeysRecon.size()); } } @@ -956,7 +956,7 @@ int VxlanMgr::createVxlanNetdevice(std::string vxlanTunnelName, std::string vni_ vlan_id.c_str()); // Case 1: Entry exist - Erase from cache & return - // Case 2: Enry does not exist - Create netDevice in Kernel + // Case 2: Entry does not exist - Create netDevice in Kernel // Case 3: Entry exist but modified - Not taken care. Will address later if (m_in_reconcile) diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index eeffeb68c1..8d82db1829 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -96,7 +96,7 @@ int main(int argc, char **argv) } catch (const std::exception& e) { - cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; return 0; } } diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index 9f5c9e1a65..2c20e3d8bc 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -188,7 +188,7 @@ int main(int argc, char **argv) } catch (const exception& e) { - cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; return 0; } } diff --git a/mclagsyncd/mclaglink.cpp b/mclagsyncd/mclaglink.cpp index adf45a5117..80353b0226 100644 --- a/mclagsyncd/mclaglink.cpp +++ b/mclagsyncd/mclaglink.cpp @@ -277,8 +277,8 @@ void MclagLink::setPortMacLearnMode(char *msg) attrs.push_back(learn_attr); if (strncmp(learn_port.c_str(), PORTCHANNEL_PREFIX, strlen(PORTCHANNEL_PREFIX)) == 0) p_lag_tbl->set(learn_port, attrs); - /*vxlan tunnel dont supported currently, for src_ip is the mandatory attribute*/ - /*else if(strncmp(learn_port.c_str(),VXLAN_TUNNEL_PREFIX,5)==0) + /* vxlan tunnel is currently not supported, for src_ip is the mandatory attribute */ + /* else if(strncmp(learn_port.c_str(),VXLAN_TUNNEL_PREFIX,5)==0) p_tnl_tbl->set(learn_port, attrs); */ else p_port_tbl->set(learn_port, attrs); diff --git a/mclagsyncd/mclagsyncd.cpp b/mclagsyncd/mclagsyncd.cpp index df65896b0c..39221d345d 100644 --- a/mclagsyncd/mclagsyncd.cpp +++ b/mclagsyncd/mclagsyncd.cpp @@ -86,7 +86,7 @@ int main(int argc, char **argv) } catch (const exception& e) { - cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; return 0; } } diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 1af94450ad..054f13a470 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -99,7 +99,7 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) /* Ignore neighbor entries with Broadcast Mac - Trigger for directed broadcast */ if (!delete_key && (MacAddress(macStr) == MacAddress("ff:ff:ff:ff:ff:ff"))) { - SWSS_LOG_INFO("Broadcast Mac recieved, ignoring for %s", ipStr); + SWSS_LOG_INFO("Broadcast Mac received, ignoring for %s", ipStr); return; } diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index f0dab62590..99e86b2ef9 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -86,7 +86,7 @@ int main(int argc, char **argv) } catch (const std::exception& e) { - cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; return 0; } } diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 798a39fc99..da361a4ea3 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -173,7 +173,7 @@ bool AclRule::validateAddPriority(string attr_name, string attr_value) char *endp = NULL; errno = 0; m_priority = (uint32_t)strtol(attr_value.c_str(), &endp, 0); - // chack conversion was successfull and the value is within the allowed range + // check conversion was successful and the value is within the allowed range status = (errno == 0) && (endp == attr_value.c_str() + attr_value.size()) && (m_priority >= m_minPriority) && @@ -2476,7 +2476,7 @@ void AclOrch::queryAclActionAttrEnumValues(const string &action_name, } } #else - /* assume all enum values are supported untill sai object api is available */ + /* assume all enum values are supported until sai object api is available */ for (size_t i = 0; i < meta->enummetadata->valuescount; i++) { m_aclEnumActionCapabilities[acl_action].insert(meta->enummetadata->values[i]); @@ -2747,7 +2747,7 @@ bool AclOrch::addAclTable(AclTable &newTable) /* If ACL table exists, remove the table first.*/ if (!removeAclTable(table_id)) { - SWSS_LOG_ERROR("Failed to remove exsiting ACL table %s before adding the new one", + SWSS_LOG_ERROR("Failed to remove existing ACL table %s before adding the new one", table_id.c_str()); return false; } diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 0e0a21262b..159fef8778 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -352,7 +352,7 @@ class AclTable { map> rules; // Set to store the ACL table port alias set portSet; - // Set to store the not cofigured ACL table port alias + // Set to store the not configured ACL table port alias set pendingPortSet; AclTable() @@ -374,9 +374,9 @@ class AclTable { bool validate(); bool create(); - // Bind the ACL table to a port which is alread linked + // Bind the ACL table to a port which is already linked bool bind(sai_object_id_t portOid); - // Unbind the ACL table to a port which is alread linked + // Unbind the ACL table to a port which is already linked bool unbind(sai_object_id_t portOid); // Bind the ACL table to all ports linked bool bind(); diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index c43ac25bd4..bc24551ca0 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -178,7 +178,7 @@ void BufferOrch::generateBufferPoolWatermarkCounterIdList(void) // is received on buffer pool watermark key under table "FLEX_COUNTER_GROUP_TABLE" // Because the SubscriberStateTable listens to the entire keyspace of "BUFFER_POOL_WATERMARK", any update // to field value tuples under key "BUFFER_POOL_WATERMARK" will cause this tuple to be heard again - // To avoid resync the coutner ID list a second time, we introduce a data member variable to mark whether + // To avoid resync the counter ID list a second time, we introduce a data member variable to mark whether // this operation has already been done or not yet if (m_isBufferPoolWatermarkCounterIdListGenerated) { diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 87334777f5..d40ca195a2 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -596,7 +596,7 @@ void CrmOrch::checkCrmThresholds() } else { - SWSS_LOG_WARN("%s Exception occured (div by Zero): Used count %u free count %u", + SWSS_LOG_WARN("%s Exception occurred (div by Zero): Used count %u free count %u", res.name.c_str(), cnt.usedCounter, cnt.availableCounter); } } diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index 31ec150276..ff2bee2e98 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -163,7 +163,7 @@ void DebugCounterOrch::doTask(Consumer& consumer) ++it; break; case task_process_status::task_failed: - SWSS_LOG_ERROR("Failed to process debug counters '%s' task, error(s) occured during execution", op.c_str()); + SWSS_LOG_ERROR("Failed to process debug counters '%s' task, error(s) occurred during execution", op.c_str()); consumer.m_toSync.erase(it++); break; default: @@ -255,7 +255,7 @@ task_process_status DebugCounterOrch::installDebugCounter(const string& counter_ addFreeCounter(counter_name, counter_type); reconcileFreeDropCounters(counter_name); - SWSS_LOG_NOTICE("Succesfully created drop counter %s", counter_name.c_str()); + SWSS_LOG_NOTICE("Successfully created drop counter %s", counter_name.c_str()); return task_process_status::task_success; } @@ -294,7 +294,7 @@ task_process_status DebugCounterOrch::uninstallDebugCounter(const string& counte m_counterNameToSwitchStatMap->hdel("", counter_name); } - SWSS_LOG_NOTICE("Succesfully deleted drop counter %s", counter_name.c_str()); + SWSS_LOG_NOTICE("Successfully deleted drop counter %s", counter_name.c_str()); return task_process_status::task_success; } @@ -451,7 +451,7 @@ void DebugCounterOrch::reconcileFreeDropCounters(const string& counter_name) createDropCounter(counter_name, counter_it->second, reasons_it->second); free_drop_counters.erase(counter_it); free_drop_reasons.erase(reasons_it); - SWSS_LOG_NOTICE("Succesfully matched drop reasons to counter %s", counter_name.c_str()); + SWSS_LOG_NOTICE("Successfully matched drop reasons to counter %s", counter_name.c_str()); } } diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 7e1c30e935..dc9efb444b 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -677,7 +677,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) if (op == "ALL") { /* - * so far only support flush all the FDB entris + * so far only support flush all the FDB entries * flush per port and flush per vlan will be added later. */ status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); @@ -741,7 +741,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) * Description: * Flushes FDB entries based on bridge_port_oid, or vlan_oid or both. * This function is called in three cases. - * 1. Port is reoved from VLAN (via SUBJECT_TYPE_VLAN_MEMBER_CHANGE) + * 1. Port is removed from VLAN (via SUBJECT_TYPE_VLAN_MEMBER_CHANGE) * 2. Bridge port OID is removed (Direct call) * 3. Port is shut down (via SUBJECT_TYPE_ */ diff --git a/orchagent/fgnhgorch.cpp b/orchagent/fgnhgorch.cpp index 7077ff4790..6ca37cad81 100644 --- a/orchagent/fgnhgorch.cpp +++ b/orchagent/fgnhgorch.cpp @@ -62,7 +62,7 @@ void FgNhgOrch::update(SubjectType type, void *cntx) SWSS_LOG_WARN("Hit unexpected condition where structs are out of sync"); } nexthop_entry->second.link_oper_state = LINK_UP; - SWSS_LOG_INFO("Updated %s assoicated with %s to state up", + SWSS_LOG_INFO("Updated %s associated with %s to state up", update->port.m_alias.c_str(), ip.to_string().c_str()); if (!m_neighOrch->getNeighborEntry(ip, nhk, macAddress)) @@ -481,9 +481,9 @@ bool FgNhgOrch::invalidNextHopInNextHopGroup(const NextHopKey& nexthop) /* setActiveBankHashBucketChanges: Sets hash buckets for active banks and called on a PER bank basis - * This function deals with a scenario where next-hop changes occured for the route, + * This function deals with a scenario where next-hop changes occurred for the route, * and the next-hop change didn't cause an entire bank to go active/inactive. - * The function uses bank_member_changes to compute the hash buckets to modify, in order to satisy the next-hop + * The function uses bank_member_changes to compute the hash buckets to modify, in order to satisfy the next-hop * availability for the route/neigh. * Eg: Prefix A had nhs 1, 2, 3 with 1, 2, 3, being equally distributed over hash buckets * 0-59(20 buckets per nh). If there was a nh removal of nh 2, this fn would equally redistribute hash buckets @@ -763,12 +763,12 @@ bool FgNhgOrch::setInactiveBankToNextAvailableActiveBank(FGNextHopGroupEntry *sy /* setInactiveBankHashBucketChanges: Sets hash buckets for inactive banks and called on a PER bank basis. - * This function deals with scenarios where next-hop changes occured for the route, + * This function deals with scenarios where next-hop changes occurred for the route, * and the next-hop change causes an active bank to become inactive, or an inactive bank to become active or * inactive bank to remain inactive. * The function uses the bank member diffs provided in bank_member_changes and uses it to compute - * the hash buckets to modify, in order to satisy the next-hop availability for the route/neigh. - * Eg: Lets assume prefix A had nhs 1, 2, 3, 4, 5, 6 with nhs being equally distirbuted over hash buckets + * the hash buckets to modify, in order to satisfy the next-hop availability for the route/neigh. + * Eg: Lets assume prefix A had nhs 1, 2, 3, 4, 5, 6 with nhs being equally distributed over hash buckets * 0-59(10 per nh). Now there was a nh deletion of 1, 2, 3 which constituted bank 0(4, 5, 6 constituted bank 1) * This function will identify that all of bank 0's nh are down and re-assign all the hash buckets(0-29) for these nhs to * nhs from bank 1, along with making local struct changes to track this for future route/neigh changes. @@ -781,7 +781,7 @@ bool FgNhgOrch::setInactiveBankHashBucketChanges(FGNextHopGroupEntry *syncd_fg_r if (bank_member_changes[bank].nhs_to_add.size() > 0) { - /* Previously inactive bank now transistions to active */ + /* Previously inactive bank now transitions to active */ syncd_fg_route_entry->syncd_fgnhg_map[bank].clear(); for (uint32_t i = fgNhgEntry->hash_bucket_indices[bank].start_index; i <= fgNhgEntry->hash_bucket_indices[bank].end_index; i++) @@ -805,7 +805,7 @@ bool FgNhgOrch::setInactiveBankHashBucketChanges(FGNextHopGroupEntry *syncd_fg_r } else if (bank_member_changes[bank].nhs_to_del.size() > 0) { - /* Previously active bank now transistions to inactive */ + /* Previously active bank now transitions to inactive */ if (!setInactiveBankToNextAvailableActiveBank(syncd_fg_route_entry, fgNhgEntry, bank, bank_member_changes, nhopgroup_members_set, ipPrefix)) { @@ -1130,7 +1130,7 @@ bool FgNhgOrch::setFgNhg(sai_object_id_t vrf_id, const IpPrefix &ipPrefix, const fgNhgEntry->hash_bucket_indices.size(), BankMemberChanges()); if (fgNhgEntry->hash_bucket_indices.size() == 0) { - /* Only happens the 1st time when hash_bucket_indices are not inited + /* Only happens the 1st time when hash_bucket_indices are not initialized */ for (auto it : fgNhgEntry->next_hops) { diff --git a/orchagent/fgnhgorch.h b/orchagent/fgnhgorch.h index 551d55ff29..42b2b7f615 100644 --- a/orchagent/fgnhgorch.h +++ b/orchagent/fgnhgorch.h @@ -83,7 +83,7 @@ typedef std::map FgNhgMembers; /* Main structure to hold user configuration */ typedef std::map FgNhgs; -/* Helper struct populated at every route change to identify the next-hop changes which occured */ +/* Helper struct populated at every route change to identify the next-hop changes which occurred */ typedef struct { std::vector nhs_to_del; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 29c774d9a9..649293c151 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -1047,7 +1047,7 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) attr.id = SAI_ROUTER_INTERFACE_ATTR_NAT_ZONE_ID; attr.value.u32 = port.m_nat_zone_id; - SWSS_LOG_INFO("Assinging NAT zone id %d to interface %s\n", attr.value.u32, port.m_alias.c_str()); + SWSS_LOG_INFO("Assigning NAT zone id %d to interface %s\n", attr.value.u32, port.m_alias.c_str()); attrs.push_back(attr); } diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 2787cffef3..68e4d0a4b8 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -81,7 +81,7 @@ void usage() cout << " -b batch_size: set consumer table pop operation batch size (default 128)" << endl; cout << " -m MAC: set switch MAC address" << endl; cout << " -i INST_ID: set the ASIC instance_id in multi-asic platform" << endl; - cout << " -s: enable synchronous mode (depreacated, use -z)" << endl; + cout << " -s: enable synchronous mode (deprecated, use -z)" << endl; cout << " -z: redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async" << endl; cout << " -f swss_rec_filename: swss record log filename(default 'swss.rec')" << endl; cout << " -j sairedis_rec_filename: sairedis record log filename(default sairedis.rec)" << endl; @@ -305,7 +305,7 @@ int main(int argc, char **argv) { SWSS_LOG_WARN("ASIC instance_id length > SAI_MAX_HARDWARE_ID_LEN, LIMITING !!"); } - // If longer, trancate into a string + // If longer, truncate into a string gAsicInstance.assign(optarg, len); } break; @@ -575,7 +575,7 @@ int main(int argc, char **argv) if (!orchDaemon->init()) { - SWSS_LOG_ERROR("Failed to initialize orchstration daemon"); + SWSS_LOG_ERROR("Failed to initialize orchestration daemon"); exit(EXIT_FAILURE); } diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index a0665406a1..0495db6363 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -613,7 +613,7 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session) } else { - // Get the firt member of the LAG + // Get the first member of the LAG Port member; string first_member_alias = *session.neighborInfo.port.m_members.begin(); m_portsOrch->getPort(first_member_alias, member); @@ -924,7 +924,7 @@ bool MirrorOrch::activateSession(const string& name, MirrorEntry& session) sai_object_id_t oid = SAI_NULL_OBJECT_ID; if (!m_policerOrch->getPolicerOid(session.policer, oid)) { - SWSS_LOG_ERROR("Faield to get policer %s", session.policer.c_str()); + SWSS_LOG_ERROR("Failed to get policer %s", session.policer.c_str()); return false; } @@ -1229,7 +1229,7 @@ void MirrorOrch::updateNeighbor(const NeighborUpdate& update) } // The function is called when SUBJECT_TYPE_FDB_CHANGE is received. -// This function will handle the case when new FDB enty is learned/added in the VLAN, +// This function will handle the case when new FDB entry is learned/added in the VLAN, // or when the old FDB entry gets removed. Only when the neighbor is VLAN will the case // be handled. void MirrorOrch::updateFdb(const FdbUpdate& update) @@ -1244,7 +1244,7 @@ void MirrorOrch::updateFdb(const FdbUpdate& update) // Check the following three conditions: // 1) mirror session is pointing to a VLAN // 2) the VLAN matches the FDB notification VLAN ID - // 3) the destination MAC matches the FDB notifaction MAC + // 3) the destination MAC matches the FDB notification MAC if (session.neighborInfo.port.m_type != Port::VLAN || session.neighborInfo.port.m_vlan_info.vlan_oid != update.entry.bv_id || session.neighborInfo.mac != update.entry.mac) @@ -1274,7 +1274,7 @@ void MirrorOrch::updateFdb(const FdbUpdate& update) activateSession(name, session); } } - // Remvoe the monitor port + // Remove the monitor port else { deactivateSession(name, session); diff --git a/orchagent/natorch.cpp b/orchagent/natorch.cpp index 68c9938f59..6bec777083 100644 --- a/orchagent/natorch.cpp +++ b/orchagent/natorch.cpp @@ -3431,7 +3431,7 @@ void NatOrch::updateAllConntrackEntries(void) { SWSS_LOG_ENTER(); - /* Send notifcations for the Single NAT entries to set timeout */ + /* Send notifications for the Single NAT entries to set timeout */ NatEntry::iterator natIter = m_natEntries.begin(); while (natIter != m_natEntries.end()) { @@ -3447,7 +3447,7 @@ void NatOrch::updateAllConntrackEntries(void) natIter++; } - /* Send notifcations for the Single NAPT entries to set timeout */ + /* Send notifications for the Single NAPT entries to set timeout */ NaptEntry::iterator naptIter = m_naptEntries.begin(); while (naptIter != m_naptEntries.end()) { @@ -3461,7 +3461,7 @@ void NatOrch::updateAllConntrackEntries(void) naptIter++; } - /* Send notifcations for the Twice NAT entries to set timeout */ + /* Send notifications for the Twice NAT entries to set timeout */ TwiceNatEntry::iterator twiceNatIter = m_twiceNatEntries.begin(); while (twiceNatIter != m_twiceNatEntries.end()) { @@ -3475,7 +3475,7 @@ void NatOrch::updateAllConntrackEntries(void) twiceNatIter++; } - /* Send notifcations for the Twice NAPT entries to set timeout */ + /* Send notifications for the Twice NAPT entries to set timeout */ TwiceNaptEntry::iterator twiceNaptIter = m_twiceNaptEntries.begin(); while (twiceNaptIter != m_twiceNaptEntries.end()) { diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index ef10d8764d..c850bf0ee6 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -822,7 +822,7 @@ bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry, bool disable) { if (status == SAI_STATUS_ITEM_NOT_FOUND) { - SWSS_LOG_ERROR("Failed to locate neigbor %s on %s, rv:%d", + SWSS_LOG_ERROR("Failed to locate neighbor %s on %s, rv:%d", m_syncdNeighbors[neighborEntry].mac.to_string().c_str(), alias.c_str(), status); return true; } diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index e26439e7b8..ea3e8e78b2 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -103,7 +103,7 @@ void Consumer::addToSync(const KeyOpFieldsValuesTuple &entry) { /* * Now we are trying to add the key-value with SET. - * We maintain maximun two values per key. + * We maintain maximum two values per key. * In case there is one key-value, it should be DEL or SET * In case there are two key-value pairs, it should be DEL then SET * The code logic is following: @@ -531,7 +531,7 @@ void Orch::logfileReopen() /* * On log rotate we will use the same file name, we are assuming that - * logrotate deamon move filename to filename.1 and we will create new + * logrotate daemon move filename to filename.1 and we will create new * empty file here. */ @@ -637,7 +637,7 @@ bool Orch::parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uin range_high = (uint32_t)stoul(range_values[1]); if (range_low >= range_high) { - SWSS_LOG_ERROR("malformed index range in:%s. left value must be less than righ value.\n", input.c_str()); + SWSS_LOG_ERROR("malformed index range in:%s. left value must be less than right value.\n", input.c_str()); return false; } } diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index c2246be280..6e4c887f2a 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -58,7 +58,7 @@ OrchDaemon::~OrchDaemon() /* * Some orchagents call other agents in their destructor. * To avoid accessing deleted agent, do deletion in reverse order. - * NOTE: This is stil not a robust solution, as order in this list + * NOTE: This is still not a robust solution, as order in this list * does not strictly match the order of construction of agents. * For a robust solution, first some cleaning/house-keeping in * orchagents management is in order. @@ -602,7 +602,7 @@ void OrchDaemon::start() /* * Try to perform orchagent state restore and dynamic states sync up if - * warm start reqeust is detected. + * warm start request is detected. */ bool OrchDaemon::warmRestoreAndSyncUp() { @@ -618,7 +618,7 @@ bool OrchDaemon::warmRestoreAndSyncUp() * * First iteration: switchorch, Port init/hostif create part of portorch, buffers configuration * - * Second iteratoin: port speed/mtu/fec_mode/pfc_asym/admin_status config, + * Second iteration: port speed/mtu/fec_mode/pfc_asym/admin_status config, * other orch(s) which wait for port to become ready. * * Third iteration: Drain remaining data that are out of order. diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 98ae16dfa5..6a0664d0f6 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -577,7 +577,7 @@ PfcWdZeroBufferHandler::ZeroBufferProfile::~ZeroBufferProfile(void) { SWSS_LOG_ENTER(); - // Destory ingress and egress prifiles and pools + // Destroy ingress and egress profiles and pools destroyZeroBufferProfile(true); destroyZeroBufferProfile(false); } diff --git a/orchagent/pfcactionhandler.h b/orchagent/pfcactionhandler.h index e7739cf341..7859709a1e 100644 --- a/orchagent/pfcactionhandler.h +++ b/orchagent/pfcactionhandler.h @@ -125,7 +125,7 @@ class PfcWdZeroBufferHandler: public PfcWdLossyHandler private: /* * Sets lock bits on port's priority group and queue - * to protect them from beeing changed by other Orch's + * to protect them from being changed by other Orch's */ void setPriorityGroupAndQueueLockFlag(Port& port, bool isLocked) const; diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index dad4ddd931..d920c21822 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -305,7 +305,7 @@ task_process_status PfcWdSwOrch::createEntry(const } else if (field == BIG_RED_SWITCH_FIELD) { - SWSS_LOG_NOTICE("Recieve brs mode set, %s", value.c_str()); + SWSS_LOG_NOTICE("Receive brs mode set, %s", value.c_str()); setBigRedSwitchMode(value); } } @@ -345,7 +345,7 @@ void PfcWdSwOrch::disableBigRedSwitchMode() SWSS_LOG_ENTER(); m_bigRedSwitchFlag = false; - // Disable pfcwdaction hanlder on each queue if exists. + // Disable pfcwdaction handler on each queue if exists. for (auto &entry : m_brsEntryMap) { @@ -422,7 +422,7 @@ void PfcWdSwOrch::enableBigRedSwitchMode() } } - // Create pfcwdaction hanlder on all the ports. + // Create pfcwdaction handler on all the ports. for (auto & it: allPorts) { Port port = it.second; @@ -907,7 +907,7 @@ bool PfcWdSwOrch::startWdActionOnQueue(const string if (m_bigRedSwitchFlag) { - SWSS_LOG_NOTICE("Big_RED_SWITCH mode is on, ingore syncd pfc watchdog notification"); + SWSS_LOG_NOTICE("Big_RED_SWITCH mode is on, ignore syncd pfc watchdog notification"); } else if (event == "storm") { diff --git a/orchagent/policerorch.cpp b/orchagent/policerorch.cpp index 3c102487c8..14b97f4ac8 100644 --- a/orchagent/policerorch.cpp +++ b/orchagent/policerorch.cpp @@ -130,7 +130,7 @@ void PolicerOrch::doTask(Consumer &consumer) if (op == SET_COMMAND) { - // Mark the opeartion as an 'update', if the policer exists. + // Mark the operation as an 'update', if the policer exists. bool update = m_syncdPolicers.find(key) != m_syncdPolicers.end(); vector attrs; diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index c45d427afd..eae00c27dc 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -468,7 +468,7 @@ bool WredMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tupl attribs.push_back(attr); } else { - SWSS_LOG_ERROR("Unkonwn wred profile field:%s", fvField(*i).c_str()); + SWSS_LOG_ERROR("Unknown wred profile field:%s", fvField(*i).c_str()); return false; } } @@ -816,7 +816,7 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) } else if (fvField(*i) == scheduler_priority_field_name) { - // TODO: The meaning is to be able to adjus priority of the given scheduler group. + // TODO: The meaning is to be able to adjust priority of the given scheduler group. // However currently SAI model does not provide such ability. } else if (fvField(*i) == scheduler_meter_type_field_name) diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index fdec0db3cf..3a88b5793c 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -54,10 +54,10 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, SwitchOrch *switchOrch, /* * ASIC specific workaround to re-calculate maximum ECMP groups - * according to diferent ECMP mode used. + * according to different ECMP mode used. * * On Mellanox platform, the maximum ECMP groups returned is the value - * under the condition that the ECMP group size is 1. Deviding this + * under the condition that the ECMP group size is 1. Dividing this * number by DEFAULT_MAX_ECMP_GROUP_SIZE gets the maximum number of * ECMP groups when the maximum ECMP group size is 32. */ @@ -549,7 +549,7 @@ void RouteOrch::doTask(Consumer& consumer) } /* Set the empty ip(s) to zero - * as IpAddress("") will construst a incorrect ip. */ + * as IpAddress("") will construct a incorrect ip. */ for (auto &ip : ipv) { if (ip.empty()) diff --git a/swssconfig/swssconfig.cpp b/swssconfig/swssconfig.cpp index 058b83a56b..a41ef0ecdd 100644 --- a/swssconfig/swssconfig.cpp +++ b/swssconfig/swssconfig.cpp @@ -89,7 +89,7 @@ bool load_json_db_data(ifstream &fs, vector &db_items) { if (el_count != arr_item.size()) { - SWSS_LOG_ERROR("Chlid elements must have both key and op entry. %s", + SWSS_LOG_ERROR("Child elements must have both key and op entry. %s", arr_item.dump().c_str()); return false; } diff --git a/teamsyncd/teamsyncd.cpp b/teamsyncd/teamsyncd.cpp index 6adedab81d..c5190f46b1 100644 --- a/teamsyncd/teamsyncd.cpp +++ b/teamsyncd/teamsyncd.cpp @@ -51,7 +51,7 @@ int main(int argc, char **argv) } catch (const std::exception& e) { - cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl; + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; return 0; } diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 302f894747..3921dfe73e 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -833,13 +833,13 @@ namespace aclorch_test } else { - // unkonw attr_value + // unknown attr_value return false; } } else { - // unknow attr_name + // unknown attr_name return false; } @@ -896,7 +896,7 @@ namespace aclorch_test } else { - // unknow attr_name + // unknown attr_name return false; } @@ -926,7 +926,7 @@ namespace aclorch_test } else { - // unknow attr_name + // unknown attr_name return false; } } @@ -999,7 +999,7 @@ namespace aclorch_test // When received ACL rule DEL_COMMAND, orchagent can delete corresponding ACL rule. // // Verify ACL table type = { L3 }, stage = { INGRESS, ENGRESS } - // Input by matchs = { SIP, DIP ...}, pkg:actions = { FORWARD, DROP ... } + // Input by matches = { SIP, DIP ...}, pkg:actions = { FORWARD, DROP ... } // TEST_F(AclOrchTest, L3Acl_Matches_Actions) { @@ -1089,7 +1089,7 @@ namespace aclorch_test // When received ACL rule DEL_COMMAND, orchagent can delete corresponding ACL rule. // // Verify ACL table type = { L3V6 }, stage = { INGRESS, ENGRESS } - // Input by matchs = { SIP, DIP ...}, pkg:actions = { FORWARD, DROP ... } + // Input by matches = { SIP, DIP ...}, pkg:actions = { FORWARD, DROP ... } // TEST_F(AclOrchTest, L3V6Acl_Matches_Actions) { diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 1ee3cdb027..a52b505b06 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -446,7 +446,7 @@ namespace portsorch_test pgConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_PG_TABLE_NAME)); pgConsumer->dumpPendingTasks(ts); - ASSERT_TRUE(ts.empty()); // PG should be proceesed now + ASSERT_TRUE(ts.empty()); // PG should be processed now ts.clear(); } @@ -571,7 +571,7 @@ namespace portsorch_test vector ts; - // check LAG, VLAN tasks were proceesed + // check LAG, VLAN tasks were processed // port table may require one more doTask iteration for (auto tableName: { APP_LAG_TABLE_NAME, diff --git a/tests/mock_tests/saispy.h b/tests/mock_tests/saispy.h index 535ef130bb..a8a5925fd7 100644 --- a/tests/mock_tests/saispy.h +++ b/tests/mock_tests/saispy.h @@ -5,7 +5,7 @@ #include "saitypes.h" -// Spy C functin pointer to std::function to access closure +// Spy C function pointer to std::function to access closure // Internal using static `spy` function pointer to invoke std::function `fake` // To make sure the convert work for multiple function in the same or different API table. // The caller shall passing to create unique SaiSpyFunction class. @@ -21,7 +21,7 @@ // auto x = SpyOn(&acl_api.get()->create_acl_table); // auto y = SpyOn(&switch_api.get()->create_switch); // -// The rest rare case is spy same function in different API table. Using differnt n value for that. +// The rest rare case is spy same function in different API table. Using different n value for that. // auto x = SpyOn<0, SAI_OBJECT_TYPE_ACL_TABLE>(&acl_api_1.get()->create_acl_table); // auto y = SpyOn<1, SAI_OBJECT_TYPE_ACL_TABLE>(&acl_api_2.get()->create_acl_table); // diff --git a/warmrestart/warmRestartAssist.cpp b/warmrestart/warmRestartAssist.cpp index ca19398577..3bb351fcb7 100644 --- a/warmrestart/warmRestartAssist.cpp +++ b/warmrestart/warmRestartAssist.cpp @@ -29,7 +29,7 @@ AppRestartAssist::AppRestartAssist(RedisPipeline *pipelineAppDB, const std::stri /* * set the default timer value. - * If the application instance privides timer value, use it if valid. + * If the application instance provides timer value, use it if valid. * Use the class default one if none is provided by application. */ if (defaultWarmStartTimerValue > MAXIMUM_WARMRESTART_TIMER_VALUE) @@ -218,7 +218,7 @@ void AppRestartAssist::insertToMap(string tableName, string key, vector vector * It is expect that the application owner keeps the order of f/v pairs - * The application ususally take this class as composition, I,e, include a instance of + * The application usually takes this class as composition, i.e. includes an instance of * this class in their classes. * A high level flow to use this class: - * 1, Include this class in the application class: appClass. + * 1. Include this class in the application class: appClass. * - * 2, Construct appClass along with this class with: + * 2. Construct appClass along with this class with: * docker name, application name, timer value etc * - * 3, Define Select s; + * 3. Define Select s; * Check if warmstart enabled, if so,read application table to cache and start timer: * if (appClass.getRestartAssist()->isWarmStartInProgress()) * { @@ -31,13 +31,13 @@ namespace swss { * appClass.getRestartAssist()->startReconcileTimer(s); * } * - * 4, Before the reconcile timer is expired, insert all requests into cache: + * 4. Before the reconcile timer is expired, insert all requests into cache: * if (m_AppRestartAssist.isWarmStartInProgress()) * { * m_AppRestartAssist.insertToMap(key, fvVector, delete_key); * } * - * 5, In the select loop, check if the reconcile timer is expired, if so, + * 5. In the select loop, check if the reconcile timer is expired, if so, * stop timer and call the reconcilation function: * Selectable *temps; * s.select(&temps); @@ -95,13 +95,13 @@ class AppRestartAssist /* * Default timer to be 5 seconds - * Overwriten by application loading this class and configurations in configDB + * Overwritten by application loading this class and configurations in configDB * Precedence ascent order: Default -> loading class with value -> configuration */ static const uint32_t DEFAULT_INTERNAL_TIMER_VALUE = 5; typedef std::map>> AppTableMap; - // cache map to store temperary application table + // cache map to store temporary application table AppTableMap appTableCacheMap; RedisPipeline *m_pipeLine; From 123807619eb667c5186f878d475d78b7183f20e8 Mon Sep 17 00:00:00 2001 From: lguohan Date: Sun, 14 Feb 2021 20:36:05 -0800 Subject: [PATCH 16/54] [test_virtual_chassis]: use wait_for to make test more robust (#1640) Signed-off-by: Guohan Lu --- tests/test_virtual_chassis.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_virtual_chassis.py b/tests/test_virtual_chassis.py index 14732e6b99..d1db2c6331 100644 --- a/tests/test_virtual_chassis.py +++ b/tests/test_virtual_chassis.py @@ -180,6 +180,7 @@ def test_chassis_system_neigh(self, vct): assert res == "", "Error configuring static neigh" asic_db = dvs.get_asic_db() + asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", 1) neighkeys = asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") assert len(neighkeys), "No neigh entries in ASIC_DB" @@ -195,8 +196,12 @@ def test_chassis_system_neigh(self, vct): # Check for presence of encap index, retrieve and store it for sync verification test_neigh_entry = asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", test_neigh) - encap_index = test_neigh_entry.get("SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX") - assert encap_index != "", "VOQ encap index is not programmed in ASIC_DB" + test_neigh_entry_attrs = asic_db.wait_for_fields("ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY", test_neigh, ["SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX"]) + print(test_neigh) + print(test_neigh_entry) + print(test_neigh_entry_attrs) + encap_index = test_neigh_entry_attrs["SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX"] + assert encap_index != "" and encap_index != None, "VOQ encap index is not programmed in ASIC_DB" break From de03dd7b9f5c4ad529d1105ebe69a26aaf75aa82 Mon Sep 17 00:00:00 2001 From: Prabhu Sreenivasan <45380242+PrabhuSreenivasan@users.noreply.github.com> Date: Mon, 15 Feb 2021 23:39:37 +0530 Subject: [PATCH 17/54] fixed unsupported resource issue (#1641) Fix Azure/sonic-buildimage#6563 **What I did** Instead of erasing unsupported resource from m_resourcesMap, mark it as unsupported. **Why I did it** Erasing an entry while iterating using range based iterator makes the iterator invalid. Also removing a resource from the m_resourcesMap impacts cross access while iterating other maps (eg crmUsedCntsTableMap). **How I verified it** Added a new unsupported resource for testing and verified the logs. --- orchagent/crmorch.cpp | 10 ++++++++-- orchagent/crmorch.h | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index d40ca195a2..659d35fb72 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -436,6 +436,12 @@ void CrmOrch::getResAvailableCounters() for (auto &res : m_resourcesMap) { + // ignore unsupported resources + if (res.second.resStatus != CrmResourceStatus::CRM_RES_SUPPORTED) + { + continue; + } + sai_attribute_t attr; attr.id = crmResSaiAvailAttrMap.at(res.first); @@ -462,8 +468,8 @@ void CrmOrch::getResAvailableCounters() SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) { - // remove unsupported resources from map - m_resourcesMap.erase(res.first); + // mark unsupported resources + res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; SWSS_LOG_NOTICE("Switch attribute %u not supported", attr.id); break; } diff --git a/orchagent/crmorch.h b/orchagent/crmorch.h index 21d039396c..7c093ffb24 100644 --- a/orchagent/crmorch.h +++ b/orchagent/crmorch.h @@ -37,6 +37,12 @@ enum class CrmThresholdType CRM_FREE, }; +enum class CrmResourceStatus +{ + CRM_RES_SUPPORTED, + CRM_RES_NOT_SUPPORTED, +}; + class CrmOrch : public Orch { public: @@ -77,6 +83,7 @@ class CrmOrch : public Orch std::map countersMap; uint32_t exceededLogCounter = 0; + CrmResourceStatus resStatus = CrmResourceStatus::CRM_RES_SUPPORTED; }; std::chrono::seconds m_pollingInterval; From 710df0c2f56273ed17b0f360416cd5e256281f27 Mon Sep 17 00:00:00 2001 From: Andriy Yurkiv <70649192+ayurkiv-nvda@users.noreply.github.com> Date: Wed, 17 Feb 2021 02:56:34 +0200 Subject: [PATCH 18/54] Add SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS counter, create new FlexCounter group (#1600) Signed-off-by: Andriy Yurkiv depends on : Azure/sonic-utilities#1355 Azure/sonic-buildimage#6444 What I did Added new option for "counterpoll" util Why I did it Add appropriate code to counterpoll/main.py How I verified it admin@arc-switch1041:~$ counterpoll pg-drop enable --> enable new counter admin@arc-switch1041:~$ counterpoll show --> check new PG_DROP_STAT counter status check counters admin@arc-switch1041:~$ redis-cli -n 2 127.0.0.1:6379[2]> HGETALL COUNTERS:oid:0x1a000000000062 1) "SAI_INGRESS_PRIORITY_GROUP_STAT_XOFF_ROOM_WATERMARK_BYTES" 2) "0" 3) "SAI_INGRESS_PRIORITY_GROUP_STAT_SHARED_WATERMARK_BYTES" 4) "0" 5) "SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS" 6) "0" --- orchagent/flexcounterorch.cpp | 1 + orchagent/portsorch.cpp | 31 ++++++++++++ orchagent/portsorch.h | 3 +- tests/test_pg_drop_counter.py | 94 +++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/test_pg_drop_counter.py diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 25dd95834e..a103433113 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -26,6 +26,7 @@ unordered_map flexCounterGroupMap = {"PFCWD", PFC_WD_FLEX_COUNTER_GROUP}, {"QUEUE_WATERMARK", QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"PG_WATERMARK", PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {"PG_DROP", PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP}, {BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"RIF", RIF_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"RIF_RATES", RIF_RATE_COUNTER_FLEX_COUNTER_GROUP}, diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index c845cafba8..89d8c53802 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -56,6 +56,7 @@ extern sai_system_port_api_t *sai_system_port_api; #define QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS 10000 #define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" #define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" +#define PG_DROP_FLEX_STAT_COUNTER_POLL_MSECS "10000" #define PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS "1000" @@ -199,6 +200,11 @@ static const vector ingressPriorityGroupWater SAI_INGRESS_PRIORITY_GROUP_STAT_SHARED_WATERMARK_BYTES, }; +static const vector ingressPriorityGroupDropStatIds = +{ + SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS +}; + static char* hostif_vlan_tag[] = { [SAI_HOSTIF_VLAN_TAG_STRIP] = "SAI_HOSTIF_VLAN_TAG_STRIP", [SAI_HOSTIF_VLAN_TAG_KEEP] = "SAI_HOSTIF_VLAN_TAG_KEEP", @@ -295,6 +301,11 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) fieldValues.emplace_back(POLL_INTERVAL_FIELD, PORT_RATE_FLEX_COUNTER_POLLING_INTERVAL_MS); fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); m_flexCounterGroupTable->set(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues); + + fieldValues.clear(); + fieldValues.emplace_back(POLL_INTERVAL_FIELD, PG_DROP_FLEX_STAT_COUNTER_POLL_MSECS); + fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); + m_flexCounterGroupTable->set(PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP, fieldValues); } catch (const runtime_error &e) { @@ -1899,6 +1910,11 @@ string PortsOrch::getPriorityGroupWatermarkFlexCounterTableKey(string key) return string(PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; } +string PortsOrch::getPriorityGroupDropPacketsFlexCounterTableKey(string key) +{ + return string(PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} + bool PortsOrch::initPort(const string &alias, const int index, const set &lane_set) { SWSS_LOG_ENTER(); @@ -4241,7 +4257,22 @@ void PortsOrch::generatePriorityGroupMapPerPort(const Port& port) vector fieldValues; fieldValues.emplace_back(PG_COUNTER_ID_LIST, counters_stream.str()); + m_flexCounterTable->set(key, fieldValues); + delimiter = ""; + std::ostringstream ingress_pg_drop_packets_counters_stream; + key = getPriorityGroupDropPacketsFlexCounterTableKey(id); + /* Add dropped packets counters to flex_counter */ + for (const auto& it: ingressPriorityGroupDropStatIds) + { + ingress_pg_drop_packets_counters_stream << delimiter << sai_serialize_ingress_priority_group_stat(it); + if (delimiter.empty()) + { + delimiter = comma; + } + } + fieldValues.clear(); + fieldValues.emplace_back(PG_COUNTER_ID_LIST, ingress_pg_drop_packets_counters_stream.str()); m_flexCounterTable->set(key, fieldValues); } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index d692b17661..627a4f4b38 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -22,7 +22,7 @@ #define QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP "QUEUE_STAT_COUNTER" #define QUEUE_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP "QUEUE_WATERMARK_STAT_COUNTER" #define PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP "PG_WATERMARK_STAT_COUNTER" - +#define PG_DROP_STAT_COUNTER_FLEX_COUNTER_GROUP "PG_DROP_STAT_COUNTER" typedef std::vector PortSupportedSpeeds; @@ -161,6 +161,7 @@ class PortsOrch : public Orch, public Subject std::string getQueueWatermarkFlexCounterTableKey(std::string s); std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s); + std::string getPriorityGroupDropPacketsFlexCounterTableKey(std::string s); std::string getPortRateFlexCounterTableKey(std::string s); shared_ptr m_counter_db; diff --git a/tests/test_pg_drop_counter.py b/tests/test_pg_drop_counter.py new file mode 100644 index 0000000000..dedee82c1a --- /dev/null +++ b/tests/test_pg_drop_counter.py @@ -0,0 +1,94 @@ +import os +import re +import time +import json +import pytest +import redis + +from swsscommon import swsscommon + +pg_drop_attr = "SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS" + +class TestPGDropCounter(object): + DEFAULT_POLL_INTERVAL = 10 + pgs = {} + + def setup_dbs(self, dvs): + self.asic_db = dvs.get_asic_db() + self.counters_db = dvs.get_counters_db() + self.config_db = dvs.get_config_db() + self.flex_db = dvs.get_flex_db() + + def set_counter(self, dvs, obj_type, obj_id, attr, val): + + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") + + r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB, + encoding="utf-8", decode_responses=True) + rid = r.hget("VIDTORID", obj_id) + + assert rid is not None + + fvp = swsscommon.FieldValuePairs([(attr, val)]) + key = rid + + # explicit convert unicode string to str for python2 + ntf.send("set_stats", str(key), fvp) + + def populate_asic(self, dvs, val): + for obj_id in self.pgs: + self.set_counter(dvs, "SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", obj_id, pg_drop_attr, val) + + def verify_value(self, dvs, obj_ids, entry_name, expected_value): + counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + table = swsscommon.Table(counters_db, "COUNTERS") + + for obj_id in obj_ids: + ret = table.get(obj_id) + + status = ret[0] + assert status + keyvalues = ret[1] + found = False + for key, value in keyvalues: + if key == entry_name: + assert value == expected_value, "Saved value not the same as expected" + found = True + assert found, "entry name %s not found" % (entry_name) + + def set_up_flex_counter(self): + pg_stats_entry = {"PG_COUNTER_ID_LIST": "{}".format(pg_drop_attr)} + for pg in self.pgs: + self.flex_db.create_entry("FLEX_COUNTER_TABLE", "PG_DROP_STAT_COUNTER:{}".format(pg), pg_stats_entry) + + fc_status_enable = {"FLEX_COUNTER_STATUS": "enable"} + + self.config_db.create_entry("FLEX_COUNTER_TABLE", "PG_DROP", fc_status_enable) + + def clear_flex_counter(self): + for pg in self.pgs: + self.flex_db.delete_entry("FLEX_COUNTER_TABLE", "PG_DROP_STAT_COUNTER:{}".format(pg)) + + self.config_db.delete_entry("FLEX_COUNTER_TABLE", "PG_DROP") + + + def test_pg_drop_counters(self, dvs): + self.setup_dbs(dvs) + self.pgs = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP") + try: + self.set_up_flex_counter() + + self.populate_asic(dvs, "0") + time.sleep(self.DEFAULT_POLL_INTERVAL) + self.verify_value(dvs, self.pgs, pg_drop_attr, "0") + + self.populate_asic(dvs, "100") + time.sleep(self.DEFAULT_POLL_INTERVAL) + self.verify_value(dvs, self.pgs, pg_drop_attr, "100") + + self.populate_asic(dvs, "123") + time.sleep(self.DEFAULT_POLL_INTERVAL) + self.verify_value(dvs, self.pgs, pg_drop_attr, "123") + finally: + self.clear_flex_counter() From 459848f1a186cc4f3690cbcd517837cb12784e1c Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Wed, 17 Feb 2021 22:50:32 +0800 Subject: [PATCH 19/54] Remove useless header in macsecorch (#1628) Signed-off-by: Ze Gan --- orchagent/macsecorch.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/orchagent/macsecorch.cpp b/orchagent/macsecorch.cpp index e43b9504f9..71e4719859 100644 --- a/orchagent/macsecorch.cpp +++ b/orchagent/macsecorch.cpp @@ -1,8 +1,6 @@ #include "macsecorch.h" #include -#include -#include #include #include #include From c49668ed834c84c0ecd43a38f34c58653b4004af Mon Sep 17 00:00:00 2001 From: Wenda Ni Date: Wed, 17 Feb 2021 06:53:06 -0800 Subject: [PATCH 20/54] [vstest/subintf] Update vs tests to validate physical port host interface vlan tag attribute (#1634) * Check vlan tag attribute for parent port being physical port Signed-off-by: Wenda Ni --- tests/test_sub_port_intf.py | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py index 8666296502..6a135e6cd1 100644 --- a/tests/test_sub_port_intf.py +++ b/tests/test_sub_port_intf.py @@ -9,6 +9,7 @@ CFG_VLAN_SUB_INTF_TABLE_NAME = "VLAN_SUB_INTERFACE" CFG_PORT_TABLE_NAME = "PORT" CFG_LAG_TABLE_NAME = "PORTCHANNEL" +CFG_LAG_MEMBER_TABLE_NAME = "PORTCHANNEL_MEMBER" STATE_PORT_TABLE_NAME = "PORT_TABLE" STATE_LAG_TABLE_NAME = "LAG_TABLE" @@ -24,6 +25,8 @@ ASIC_NEXT_HOP_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" ASIC_NEXT_HOP_GROUP_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" ASIC_NEXT_HOP_GROUP_MEMBER_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" +ASIC_LAG_MEMBER_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER" +ASIC_HOSTIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF" ADMIN_STATUS = "admin_status" @@ -36,6 +39,7 @@ class TestSubPortIntf(object): SUB_PORT_INTERFACE_UNDER_TEST = "Ethernet64.10" LAG_SUB_PORT_INTERFACE_UNDER_TEST = "PortChannel1.20" + LAG_MEMBERS_UNDER_TEST = ["Ethernet68", "Ethernet72"] IPV4_ADDR_UNDER_TEST = "10.0.0.33/31" IPV4_TOME_UNDER_TEST = "10.0.0.33/32" @@ -91,6 +95,13 @@ def create_sub_port_intf_profile(self, sub_port_intf_name): self.config_db.create_entry(CFG_VLAN_SUB_INTF_TABLE_NAME, sub_port_intf_name, fvs) + def add_lag_members(self, lag, members): + fvs = {"NULL": "NULL"} + + for member in members: + key = "{}|{}".format(lag, member) + self.config_db.create_entry(CFG_LAG_MEMBER_TABLE_NAME, key, fvs) + def add_sub_port_intf_ip_addr(self, sub_port_intf_name, ip_addr): fvs = {"NULL": "NULL"} @@ -102,6 +113,11 @@ def set_sub_port_intf_admin_status(self, sub_port_intf_name, status): self.config_db.create_entry(CFG_VLAN_SUB_INTF_TABLE_NAME, sub_port_intf_name, fvs) + def remove_lag_members(self, lag, members): + for member in members: + key = "{}|{}".format(lag, member) + self.config_db.delete_entry(CFG_LAG_MEMBER_TABLE_NAME, key) + def remove_sub_port_intf_profile(self, sub_port_intf_name): self.config_db.delete_entry(CFG_VLAN_SUB_INTF_TABLE_NAME, sub_port_intf_name) @@ -186,13 +202,19 @@ def _test_sub_port_intf_creation(self, dvs, sub_port_intf_name): vlan_id = substrs[1] if parent_port.startswith(ETHERNET_PREFIX): state_tbl_name = STATE_PORT_TABLE_NAME + phy_ports = [parent_port] else: assert parent_port.startswith(LAG_PREFIX) state_tbl_name = STATE_LAG_TABLE_NAME + phy_ports = self.LAG_MEMBERS_UNDER_TEST old_rif_oids = self.get_oids(ASIC_RIF_TABLE) self.set_parent_port_admin_status(dvs, parent_port, "up") + # Add lag members to test physical port host interface vlan tag attribute + if parent_port.startswith(LAG_PREFIX): + self.add_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) + self.asic_db.wait_for_n_keys(ASIC_LAG_MEMBER_TABLE, len(self.LAG_MEMBERS_UNDER_TEST)) self.create_sub_port_intf_profile(sub_port_intf_name) # Verify that sub port interface state ok is pushed to STATE_DB by Intfmgrd @@ -218,10 +240,23 @@ def _test_sub_port_intf_creation(self, dvs, sub_port_intf_name): rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + # Verify physical port host interface vlan tag attribute + fv_dict = { + "SAI_HOSTIF_ATTR_VLAN_TAG": "SAI_HOSTIF_VLAN_TAG_KEEP", + } + for phy_port in phy_ports: + hostif_oid = dvs.asicdb.hostifnamemap[phy_port] + self.check_sub_port_intf_fvs(self.asic_db, ASIC_HOSTIF_TABLE, hostif_oid, fv_dict) + # Remove a sub port interface self.remove_sub_port_intf_profile(sub_port_intf_name) self.check_sub_port_intf_profile_removal(rif_oid) + # Remove lag members from lag parent port + if parent_port.startswith(LAG_PREFIX): + self.remove_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) + self.asic_db.wait_for_n_keys(ASIC_LAG_MEMBER_TABLE, 0) + def test_sub_port_intf_creation(self, dvs): self.connect_dbs(dvs) @@ -427,13 +462,19 @@ def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name): parent_port = substrs[0] if parent_port.startswith(ETHERNET_PREFIX): state_tbl_name = STATE_PORT_TABLE_NAME + phy_ports = [parent_port] else: assert parent_port.startswith(LAG_PREFIX) state_tbl_name = STATE_LAG_TABLE_NAME + phy_ports = self.LAG_MEMBERS_UNDER_TEST old_rif_oids = self.get_oids(ASIC_RIF_TABLE) self.set_parent_port_admin_status(dvs, parent_port, "up") + # Add lag members to test physical port host interface vlan tag attribute + if parent_port.startswith(LAG_PREFIX): + self.add_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) + self.asic_db.wait_for_n_keys(ASIC_LAG_MEMBER_TABLE, len(self.LAG_MEMBERS_UNDER_TEST)) self.create_sub_port_intf_profile(sub_port_intf_name) self.add_sub_port_intf_ip_addr(sub_port_intf_name, self.IPV4_ADDR_UNDER_TEST) @@ -471,6 +512,19 @@ def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name): # Verify that sub port router interface entry is removed from ASIC_DB self.check_sub_port_intf_key_removal(self.asic_db, ASIC_RIF_TABLE, rif_oid) + # Verify physical port host interface vlan tag attribute + fv_dict = { + "SAI_HOSTIF_ATTR_VLAN_TAG": "SAI_HOSTIF_VLAN_TAG_STRIP", + } + for phy_port in phy_ports: + hostif_oid = dvs.asicdb.hostifnamemap[phy_port] + self.check_sub_port_intf_fvs(self.asic_db, ASIC_HOSTIF_TABLE, hostif_oid, fv_dict) + + # Remove lag members from lag parent port + if parent_port.startswith(LAG_PREFIX): + self.remove_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) + self.asic_db.wait_for_n_keys(ASIC_LAG_MEMBER_TABLE, 0) + def test_sub_port_intf_removal(self, dvs): self.connect_dbs(dvs) From d407bc832e30e45f7e5d97b1f71581906061b31a Mon Sep 17 00:00:00 2001 From: Guohan Lu Date: Sat, 2 Jan 2021 11:36:25 -0800 Subject: [PATCH 21/54] [vstest]: add dvs_route fixture Signed-off-by: Guohan Lu --- tests/conftest.py | 15 +++++++++++---- tests/dvslib/dvs_route.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 tests/dvslib/dvs_route.py diff --git a/tests/conftest.py b/tests/conftest.py index 357cb4f15f..79c0e6c968 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,7 @@ from dvslib.dvs_database import DVSDatabase from dvslib.dvs_common import PollingConfig, wait_for_result from dvslib.dvs_acl import DVSAcl +from dvslib.dvs_route import DVSRoute from dvslib import dvs_vlan from dvslib import dvs_lag from dvslib import dvs_mirror @@ -1096,7 +1097,7 @@ def getVlanOid(self, vlanId): vlan_oid = str(k) break return vlan_oid - + # deps: acl_portchannel, fdb def getCrmCounterValue(self, key, counter): counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, self.redis_sock, 0) @@ -1459,7 +1460,7 @@ def get_chassis_instance_port_statuses(self): chassis_container_name = device_info["hostname"] + "." + self.ns port_info = config["PORT"] - + for port, config in port_info.items(): if "admin_status" not in config: continue @@ -1468,13 +1469,13 @@ def get_chassis_instance_port_statuses(self): instance_to_port_status_map[chassis_container_name] = [] instance_to_port_status_map[chassis_container_name].append((port, config.get("admin_status"))) - + return instance_to_port_status_map def handle_chassis_connections(self): if self.oper != "create": return - + instance_to_port_status_map = self.get_chassis_instance_port_statuses() for chassis_instance, port_statuses in instance_to_port_status_map.items(): if chassis_instance not in self.dvss: @@ -1590,6 +1591,12 @@ def dvs_acl(request, dvs) -> DVSAcl: dvs.get_state_db(), dvs.get_counters_db()) +@pytest.fixture(scope="class") +def dvs_route(request, dvs) -> DVSRoute: + return DVSRoute(dvs.get_asic_db(), + dvs.get_config_db()) + + # FIXME: The rest of these also need to be reverted back to normal fixtures to # appease the linter. @pytest.yield_fixture(scope="class") diff --git a/tests/dvslib/dvs_route.py b/tests/dvslib/dvs_route.py new file mode 100644 index 0000000000..2b3b87cbe7 --- /dev/null +++ b/tests/dvslib/dvs_route.py @@ -0,0 +1,28 @@ +import json +from dvslib.dvs_common import wait_for_result + +class DVSRoute(object): + def __init__(self, adb, cdb): + self.asic_db = adb + self.config_db = cdb + + def check_asicdb_route_entries(self, destinations): + def _access_function(): + route_entries = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + route_destinations = [json.loads(route_entry)["dest"] + for route_entry in route_entries] + return (all(destination in route_destinations for destination in destinations), None) + + wait_for_result(_access_function) + + keys = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + + return [k for k in keys if json.loads(k)['dest'] in destinations] + + def check_asicdb_deleted_route_entries(self, destinations): + def _access_function(): + route_entries = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + route_destinations = [json.loads(route_entry)["dest"] for route_entry in route_entries] + return (all(destination not in route_destinations for destination in destinations), None) + + wait_for_result(_access_function) From 20ee836058e975c4c73df5c0c7b9ded0cc69c06e Mon Sep 17 00:00:00 2001 From: Guohan Lu Date: Wed, 17 Feb 2021 07:40:32 -0800 Subject: [PATCH 22/54] [vstest/nhg]: use dvs_route fixture to make test_nhg more robust Signed-off-by: Guohan Lu --- tests/test_nhg.py | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/tests/test_nhg.py b/tests/test_nhg.py index 610246d98e..485fc7661c 100644 --- a/tests/test_nhg.py +++ b/tests/test_nhg.py @@ -10,7 +10,7 @@ class TestNextHopGroup(object): - def test_route_nhg(self, dvs, testlog): + def test_route_nhg(self, dvs, dvs_route, testlog): config_db = dvs.get_config_db() fvs = {"NULL": "NULL"} config_db.create_entry("INTERFACE", "Ethernet0", fvs) @@ -35,32 +35,23 @@ def test_route_nhg(self, dvs, testlog): assert dvs.servers[1].runcmd("ip link set up dev eth0") == 0 assert dvs.servers[2].runcmd("ip link set up dev eth0") == 0 + rtprefix = "2.2.2.0/24" + app_db = dvs.get_app_db() ps = swsscommon.ProducerStateTable(app_db.db_connection, "ROUTE_TABLE") asic_db = dvs.get_asic_db() - asic_routes_count = len(asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY")) + + dvs_route.check_asicdb_deleted_route_entries([rtprefix]) fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3,10.0.0.5"), ("ifname", "Ethernet0,Ethernet4,Ethernet8")]) - ps.set("2.2.2.0/24", fvs) + ps.set(rtprefix, fvs) # check if route was propagated to ASIC DB - - asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", asic_routes_count + 1) - keys = asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") - - found_route = False - for k in keys: - rt_key = json.loads(k) - - if rt_key['dest'] == "2.2.2.0/24": - found_route = True - break - - assert found_route + rtkeys = dvs_route.check_asicdb_route_entries([rtprefix]) # assert the route points to next hop group - fvs = asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", k) + fvs = asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", rtkeys[0]) nhgid = fvs["SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID"] @@ -112,10 +103,10 @@ def test_route_nhg(self, dvs, testlog): assert fvs["SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID"] == nhgid # Remove route 2.2.2.0/24 - ps._del("2.2.2.0/24") + ps._del(rtprefix) # Wait for route 2.2.2.0/24 to be removed - asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", asic_routes_count) + dvs_route.check_asicdb_deleted_route_entries([rtprefix]) def test_route_nhg_exhaust(self, dvs, testlog): """ From 16cd6c2e899272d226849d99d3b07b210e31caa4 Mon Sep 17 00:00:00 2001 From: Lior Avramov <73036155+liorghub@users.noreply.github.com> Date: Mon, 22 Feb 2021 16:56:01 +0200 Subject: [PATCH 23/54] [orchagent] Increase SAI REDIS response timeout to support FW upgrade during init (Mellanox only). (#1637) Signed-off-by: liora Co-authored-by: liora --- orchagent/saihelper.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index eac1414b0b..0ea5ecef0f 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -19,6 +19,7 @@ extern "C" { #include "timestamp.h" #include "sai_serialize.h" #include "saihelper.h" +#include "orch.h" using namespace std; using namespace swss; @@ -27,6 +28,7 @@ using namespace swss; #define STR(s) _STR(s) #define CONTEXT_CFG_FILE "/usr/share/sonic/hwsku/context_config.json" +#define SAI_REDIS_SYNC_OPERATION_RESPONSE_TIMEOUT (480*1000) // hwinfo = "INTERFACE_NAME/PHY ID", mii_ioctl_data->phy_id is a __u16 #define HWINFO_MAX_SIZE IFNAMSIZ + 1 + 5 @@ -278,6 +280,26 @@ void initSaiRedis(const string &record_location, const std::string &record_filen } SWSS_LOG_NOTICE("Enable redis pipeline"); + char *platform = getenv("platform"); + if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + { + /* We set this long timeout in order for Orchagent to wait enough time for + * response from syncd. It is needed since in init, systemd syncd startup + * script first calls FW upgrade script (that might take up to 7 minutes + * in systems with Gearbox) and only then launches syncd container */ + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + attr.value.u64 = SAI_REDIS_SYNC_OPERATION_RESPONSE_TIMEOUT; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI REDIS response timeout"); + exit(EXIT_FAILURE); + } + + SWSS_LOG_NOTICE("SAI REDIS response timeout set successfully to %" PRIu64 " ", attr.value.u64); + } + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_INIT_VIEW; status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); @@ -288,6 +310,22 @@ void initSaiRedis(const string &record_location, const std::string &record_filen exit(EXIT_FAILURE); } SWSS_LOG_NOTICE("Notify syncd INIT_VIEW"); + + if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + { + /* Set timeout back to the default value */ + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + attr.value.u64 = SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set SAI REDIS response timeout"); + exit(EXIT_FAILURE); + } + + SWSS_LOG_NOTICE("SAI REDIS response timeout set successfully to %" PRIu64 " ", attr.value.u64); + } } sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy) From 97b3913cd459b9f29e740d2189dc9d47a9ec6309 Mon Sep 17 00:00:00 2001 From: vmittal-msft <46945843+vmittal-msft@users.noreply.github.com> Date: Mon, 22 Feb 2021 16:16:22 -0800 Subject: [PATCH 24/54] Updated PFCWD to use single ACL table for PFCWD and MUX (#1620) Updated PFCWD to use single ACL table for PFCWD and MUX --- orchagent/aclorch.cpp | 105 ++++++++++++++++++++++++++++---- orchagent/aclorch.h | 19 +++++- orchagent/pfcactionhandler.cpp | 84 ++++++++++++++++++++----- orchagent/pfcactionhandler.h | 3 +- orchagent/pfcwdorch.cpp | 3 +- tests/dvslib/dvs_acl.py | 27 ++++++++ tests/mock_tests/aclorch_ut.cpp | 2 +- tests/test_pfcwd.py | 84 +++++++++++++++++++++++++ 8 files changed, 294 insertions(+), 33 deletions(-) create mode 100644 tests/test_pfcwd.py diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index da361a4ea3..2f14c61349 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -109,7 +109,8 @@ static acl_table_type_lookup_t aclTableTypeLookUp = { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE }, { TABLE_TYPE_DTEL_FLOW_WATCHLIST, ACL_TABLE_DTEL_FLOW_WATCHLIST }, { TABLE_TYPE_DTEL_DROP_WATCHLIST, ACL_TABLE_DTEL_DROP_WATCHLIST }, - { TABLE_TYPE_MCLAG, ACL_TABLE_MCLAG } + { TABLE_TYPE_MCLAG, ACL_TABLE_MCLAG }, + { TABLE_TYPE_DROP, ACL_TABLE_DROP } }; static acl_stage_type_lookup_t aclStageLookUp = @@ -620,6 +621,23 @@ bool AclRule::remove() return res; } +void AclRule::updateInPorts() +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS; + attr.value = m_matches[SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS]; + attr.value.aclfield.enable = true; + + status = sai_acl_api->set_acl_entry_attribute(m_ruleOid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update ACL rule %s, rv:%d", m_id.c_str(), status); + } +} + AclRuleCounters AclRule::getCounters() { SWSS_LOG_ENTER(); @@ -675,7 +693,8 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir type != ACL_TABLE_MIRROR_DSCP && type != ACL_TABLE_DTEL_FLOW_WATCHLIST && type != ACL_TABLE_DTEL_DROP_WATCHLIST && - type != ACL_TABLE_MCLAG) + type != ACL_TABLE_MCLAG && + type != ACL_TABLE_DROP) { throw runtime_error("Unknown table type"); } @@ -723,6 +742,10 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { return make_shared(acl, rule, table, type); } + else if (type == ACL_TABLE_DROP) + { + return make_shared(acl, rule, table, type); + } throw runtime_error("Wrong combination of table type and action in rule " + rule); } @@ -998,7 +1021,6 @@ void AclRuleL3::update(SubjectType, void *) // Do nothing } - AclRulePfcwd::AclRulePfcwd(AclOrch *aclOrch, string rule, string table, acl_table_type_t type, bool createCounter) : AclRuleL3(aclOrch, rule, table, type, createCounter) { @@ -1006,12 +1028,6 @@ AclRulePfcwd::AclRulePfcwd(AclOrch *aclOrch, string rule, string table, acl_tabl bool AclRulePfcwd::validateAddMatch(string attr_name, string attr_value) { - if (attr_name != MATCH_TC) - { - SWSS_LOG_ERROR("%s is not supported for the tables of type Pfcwd", attr_name.c_str()); - return false; - } - return AclRule::validateAddMatch(attr_name, attr_value); } @@ -1326,7 +1342,7 @@ bool AclTable::create() vector bpoint_list; // PFC watch dog ACLs are only applied to port - if (type == ACL_TABLE_PFCWD) + if ((type == ACL_TABLE_PFCWD) || (type == ACL_TABLE_DROP)) { bpoint_list = { SAI_ACL_BIND_POINT_TYPE_PORT }; } @@ -1340,7 +1356,7 @@ bool AclTable::create() attr.value.s32list.list = bpoint_list.data(); table_attrs.push_back(attr); - if (type == ACL_TABLE_PFCWD) + if ((type == ACL_TABLE_PFCWD) || (type == ACL_TABLE_DROP)) { attr.id = SAI_ACL_TABLE_ATTR_FIELD_TC; attr.value.booldata = true; @@ -1349,7 +1365,14 @@ bool AclTable::create() attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; attr.value.s32 = (stage == ACL_STAGE_INGRESS) ? SAI_ACL_STAGE_INGRESS : SAI_ACL_STAGE_EGRESS; table_attrs.push_back(attr); - + + if (stage == ACL_STAGE_INGRESS) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_IN_PORTS; + attr.value.booldata = true; + table_attrs.push_back(attr); + } + sai_status_t status = sai_acl_api->create_acl_table(&m_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); if (status == SAI_STATUS_SUCCESS) @@ -2919,6 +2942,64 @@ bool AclOrch::removeAclRule(string table_id, string rule_id) return m_AclTables[table_oid].remove(rule_id); } +bool AclOrch::updateAclRule(shared_ptr rule, string table_id, string attr_name, void *data, bool oper) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t table_oid = getTableById(table_id); + string attr_value; + + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to update ACL rule in ACL table %s. Table doesn't exist", table_id.c_str()); + return false; + } + + switch (aclMatchLookup[attr_name]) + { + case SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS: + { + sai_object_id_t port_oid = *(sai_object_id_t *)data; + vector in_ports = rule->getInPorts(); + + if (oper == RULE_OPER_ADD) + { + in_ports.push_back(port_oid); + } + else + { + for (auto port_iter = in_ports.begin(); port_iter != in_ports.end(); port_iter++) + { + if (*port_iter == port_oid) + { + in_ports.erase(port_iter); + break; + } + } + } + + for (const auto& port_iter: in_ports) + { + Port p; + gPortsOrch->getPort(port_iter, p); + attr_value += p.m_alias; + attr_value += ','; + } + attr_value.pop_back(); + + rule->validateAddMatch(MATCH_IN_PORTS, attr_value); + m_AclTables[table_oid].rules[rule->getId()]->updateInPorts(); + } + break; + + default: + SWSS_LOG_ERROR("Acl rule update not supported for attr name %s", attr_name.c_str()); + break; + } + + return true; +} + bool AclOrch::isCombinedMirrorV6Table() { return m_isCombinedMirrorV6Table; diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 159fef8778..267fb6eeb9 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -36,6 +36,7 @@ #define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" #define TABLE_TYPE_MCLAG "MCLAG" #define TABLE_TYPE_MUX "MUX" +#define TABLE_TYPE_DROP "DROP" #define RULE_PRIORITY "PRIORITY" #define MATCH_IN_PORTS "IN_PORTS" @@ -103,6 +104,9 @@ #define IP_TYPE_ARP_REPLY "ARP_REPLY" #define MLNX_MAX_RANGES_COUNT 16 +#define INGRESS_TABLE_DROP "IngressTableDrop" +#define RULE_OPER_ADD 0 +#define RULE_OPER_DELETE 1 typedef enum { @@ -117,7 +121,8 @@ typedef enum ACL_TABLE_DTEL_FLOW_WATCHLIST, ACL_TABLE_DTEL_DROP_WATCHLIST, ACL_TABLE_MCLAG, - ACL_TABLE_MUX + ACL_TABLE_MUX, + ACL_TABLE_DROP } acl_table_type_t; typedef map acl_table_type_lookup_t; @@ -196,6 +201,7 @@ class AclRule virtual bool create(); virtual bool remove(); virtual void update(SubjectType, void *) = 0; + virtual void updateInPorts(); virtual AclRuleCounters getCounters(); string getId() @@ -213,6 +219,11 @@ class AclRule return m_counterOid; } + vector getInPorts() + { + return m_inPorts; + } + static shared_ptr makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); virtual ~AclRule() {} @@ -417,7 +428,6 @@ class AclOrch : public Orch, public Observer return m_countersTable; } - // FIXME: Add getters for them? I'd better to add a common directory of orch objects and use it everywhere MirrorOrch *m_mirrorOrch; NeighOrch *m_neighOrch; @@ -429,6 +439,7 @@ class AclOrch : public Orch, public Observer bool updateAclTable(AclTable ¤tTable, AclTable &newTable); bool addAclRule(shared_ptr aclRule, string table_id); bool removeAclRule(string table_id, string rule_id); + bool updateAclRule(shared_ptr aclRule, string table_id, string attr_name, void *data, bool oper); bool isCombinedMirrorV6Table(); bool isAclActionSupported(acl_stage_type_t stage, sai_acl_action_type_t action) const; @@ -443,6 +454,10 @@ class AclOrch : public Orch, public Observer static bool getAclBindPortId(Port& port, sai_object_id_t& port_id); using Orch::doTask; // Allow access to the basic doTask + map getAclTables() + { + return m_AclTables; + } private: SwitchOrch *m_switchOrch; diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 6a0664d0f6..6bc65a6114 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -225,35 +225,50 @@ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, { SWSS_LOG_ENTER(); - acl_table_type_t table_type = ACL_TABLE_PFCWD; + acl_table_type_t table_type; + sai_object_id_t table_oid; - // There is one handler instance per queue ID string queuestr = to_string(queueId); - m_strIngressTable = "IngressTable_PfcWdAclHandler_" + queuestr; - m_strEgressTable = "EgressTable_PfcWdAclHandler_" + queuestr; m_strRule = "Rule_PfcWdAclHandler_" + queuestr; + // Ingress table/rule creation + table_type = ACL_TABLE_DROP; + m_strIngressTable = INGRESS_TABLE_DROP; auto found = m_aclTables.find(m_strIngressTable); if (found == m_aclTables.end()) { // First time of handling PFC for this queue, create ACL table, and bind createPfcAclTable(port, m_strIngressTable, true); shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strIngressTable, table_type); - createPfcAclRule(newRule, queueId, m_strIngressTable); + createPfcAclRule(newRule, queueId, m_strIngressTable, port); } else { - // Otherwise just bind ACL table with the port - found->second.bind(port); + table_oid = gAclOrch->getTableById(m_strIngressTable); + map table_map = gAclOrch->getAclTables(); + auto rule_iter = table_map[table_oid].rules.find(m_strRule); + + if (rule_iter == table_map[table_oid].rules.end()) + { + shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strIngressTable, table_type); + createPfcAclRule(newRule, queueId, m_strIngressTable, port); + } + else + { + gAclOrch->updateAclRule(rule_iter->second, m_strIngressTable, MATCH_IN_PORTS, &port, RULE_OPER_ADD); + } } + // Egress table/rule creation + table_type = ACL_TABLE_PFCWD; + m_strEgressTable = "EgressTable_PfcWdAclHandler_" + queuestr; found = m_aclTables.find(m_strEgressTable); if (found == m_aclTables.end()) { // First time of handling PFC for this queue, create ACL table, and bind createPfcAclTable(port, m_strEgressTable, false); shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strEgressTable, table_type); - createPfcAclRule(newRule, queueId, m_strEgressTable); + createPfcAclRule(newRule, queueId, m_strEgressTable, port); } else { @@ -266,11 +281,24 @@ PfcWdAclHandler::~PfcWdAclHandler(void) { SWSS_LOG_ENTER(); - auto found = m_aclTables.find(m_strIngressTable); - found->second.unbind(getPort()); + sai_object_id_t table_oid = gAclOrch->getTableById(m_strIngressTable); + map table_map = gAclOrch->getAclTables(); + auto rule_iter = table_map[table_oid].rules.find(m_strRule); - found = m_aclTables.find(m_strEgressTable); - found->second.unbind(getPort()); + vector port_set = rule_iter->second->getInPorts(); + sai_object_id_t port = getPort(); + + if ((port_set.size() == 1) && (port_set[0] == port)) + { + gAclOrch->removeAclRule(m_strIngressTable, m_strRule); + } + else + { + gAclOrch->updateAclRule(rule_iter->second, m_strIngressTable, MATCH_IN_PORTS, &port, RULE_OPER_DELETE); + } + + auto found = m_aclTables.find(m_strEgressTable); + found->second.unbind(port); } void PfcWdAclHandler::clear() @@ -295,14 +323,24 @@ void PfcWdAclHandler::createPfcAclTable(sai_object_id_t port, string strTable, b assert(inserted.second); AclTable& aclTable = inserted.first->second; - aclTable.type = ACL_TABLE_PFCWD; aclTable.link(port); aclTable.id = strTable; - aclTable.stage = ingress ? ACL_STAGE_INGRESS : ACL_STAGE_EGRESS; + + if (ingress) + { + aclTable.type = ACL_TABLE_DROP; + aclTable.stage = ACL_STAGE_INGRESS; + } + else + { + aclTable.type = ACL_TABLE_PFCWD; + aclTable.stage = ACL_STAGE_EGRESS; + } + gAclOrch->addAclTable(aclTable); } -void PfcWdAclHandler::createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable) +void PfcWdAclHandler::createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable, sai_object_id_t portOid) { SWSS_LOG_ENTER(); @@ -316,6 +354,22 @@ void PfcWdAclHandler::createPfcAclRule(shared_ptr rule, uint8_t qu attr_value = to_string(queueId); rule->validateAddMatch(attr_name, attr_value); + // Add MATCH_IN_PORTS as match criteria for ingress table + if (strTable == INGRESS_TABLE_DROP) + { + Port p; + attr_name = MATCH_IN_PORTS; + + if (!gPortsOrch->getPort(portOid, p)) + { + SWSS_LOG_ERROR("Failed to get port structure from port oid 0x%" PRIx64, portOid); + return; + } + + attr_value = p.m_alias; + rule->validateAddMatch(attr_name, attr_value); + } + attr_name = ACTION_PACKET_ACTION; attr_value = PACKET_ACTION_DROP; rule->validateAddAction(attr_name, attr_value); diff --git a/orchagent/pfcactionhandler.h b/orchagent/pfcactionhandler.h index 7859709a1e..e381a798c6 100644 --- a/orchagent/pfcactionhandler.h +++ b/orchagent/pfcactionhandler.h @@ -111,7 +111,8 @@ class PfcWdAclHandler: public PfcWdLossyHandler string m_strEgressTable; string m_strRule; void createPfcAclTable(sai_object_id_t port, string strTable, bool ingress); - void createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable); + void createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable, sai_object_id_t port); + void updatePfcAclRule(shared_ptr rule, uint8_t queueId, string strTable, vector port); }; // PFC queue that implements drop action by draining queue with buffer of zero size diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index d920c21822..ca37a85be2 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -177,8 +177,7 @@ task_process_status PfcWdOrch::createEntry(const st uint32_t detectionTime = 0; uint32_t restorationTime = 0; // According to requirements, drop action is default - PfcWdAction action = PfcWdAction::PFC_WD_ACTION_DROP; - + PfcWdAction action = PfcWdAction::PFC_WD_ACTION_DROP; Port port; if (!gPortsOrch->getPort(key, port)) { diff --git a/tests/dvslib/dvs_acl.py b/tests/dvslib/dvs_acl.py index 2b48c3b76c..3a68142b46 100644 --- a/tests/dvslib/dvs_acl.py +++ b/tests/dvslib/dvs_acl.py @@ -254,6 +254,33 @@ def create_acl_rule( self.config_db.create_entry("ACL_RULE", "{}|{}".format(table_name, rule_name), fvs) + def update_acl_rule( + self, + table_name: str, + rule_name: str, + qualifiers: Dict[str, str], + action: str = "FORWARD", + priority: str = "2020" + ) -> None: + """Create a new ACL rule in the given table. + + Args: + table_name: The name of the ACL table to add the rule to. + rule_name: The name of the ACL rule. + qualifiers: The list of qualifiers to add to the rule. + action: The packet action of the rule. + priority: The priority of the rule. + """ + fvs = { + "priority": priority, + "PACKET_ACTION": action + } + + for k, v in qualifiers.items(): + fvs[k] = v + + self.config_db.update_entry("ACL_RULE", "{}|{}".format(table_name, rule_name), fvs) + def create_redirect_acl_rule( self, table_name: str, diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 3921dfe73e..bed031396e 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -116,7 +116,7 @@ namespace aclorch_test { "SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT", "true" }, { "SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS", "true" }, { "SAI_ACL_TABLE_ATTR_FIELD_ACL_RANGE_TYPE", "2:SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE,SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE" }, - { "SAI_ACL_TABLE_ATTR_ACL_STAGE", "SAI_ACL_STAGE_INGRESS" } }); + { "SAI_ACL_TABLE_ATTR_ACL_STAGE", "SAI_ACL_STAGE_INGRESS" }}); SaiAttributeList attr_list(SAI_OBJECT_TYPE_ACL_TABLE, v, false); ASSERT_TRUE(Check::AttrListEq(SAI_OBJECT_TYPE_ACL_TABLE, res->attr_list, attr_list)); diff --git a/tests/test_pfcwd.py b/tests/test_pfcwd.py new file mode 100644 index 0000000000..c569bc8a43 --- /dev/null +++ b/tests/test_pfcwd.py @@ -0,0 +1,84 @@ +import redis +import time +import os +import pytest +import re +import json +from swsscommon import swsscommon + +PFCWD_TABLE_NAME = "DROP_TEST_TABLE" +PFCWD_TABLE_TYPE = "DROP" +PFCWD_TC = ["3", "4"] +PFCWD_RULE_NAME_1 = "DROP_TEST_RULE_1" +PFCWD_RULE_NAME_2 = "DROP_TEST_RULE_2" + +class TestPfcWd: + def test_PfcWdAclCreationDeletion(self, dvs, dvs_acl, testlog): + try: + dvs_acl.create_acl_table(PFCWD_TABLE_NAME, PFCWD_TABLE_TYPE, ["Ethernet0","Ethernet8", "Ethernet16", "Ethernet24"], stage="ingress") + + config_qualifiers = { + "TC" : PFCWD_TC[0], + "IN_PORTS": "Ethernet0" + } + + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_TC" : dvs_acl.get_simple_qualifier_comparator("3&mask:0xff"), + "SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS": dvs_acl.get_port_list_comparator(["Ethernet0"]) + } + + dvs_acl.create_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_1, config_qualifiers, action="DROP") + time.sleep(5) + dvs_acl.verify_acl_rule(expected_sai_qualifiers, action="DROP") + + config_qualifiers = { + "TC" : PFCWD_TC[0], + "IN_PORTS": "Ethernet0,Ethernet16" + } + + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_TC" : dvs_acl.get_simple_qualifier_comparator("3&mask:0xff"), + "SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS": dvs_acl.get_port_list_comparator(["Ethernet0","Ethernet16"]) + } + + dvs_acl.update_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_1, config_qualifiers, action="DROP") + time.sleep(5) + dvs_acl.verify_acl_rule(expected_sai_qualifiers, action="DROP") + dvs_acl.remove_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_1) + + config_qualifiers = { + "TC" : PFCWD_TC[1], + "IN_PORTS": "Ethernet8" + } + + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_TC" : dvs_acl.get_simple_qualifier_comparator("4&mask:0xff"), + "SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS": dvs_acl.get_port_list_comparator(["Ethernet8"]), + } + + dvs_acl.create_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_2, config_qualifiers, action="DROP") + time.sleep(5) + dvs_acl.verify_acl_rule(expected_sai_qualifiers, action="DROP") + + config_qualifiers = { + "TC" : PFCWD_TC[1], + "IN_PORTS": "Ethernet8,Ethernet24" + } + + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_TC" : dvs_acl.get_simple_qualifier_comparator("4&mask:0xff"), + "SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS": dvs_acl.get_port_list_comparator(["Ethernet8","Ethernet24"]), + } + + dvs_acl.update_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_2, config_qualifiers, action="DROP") + time.sleep(5) + dvs_acl.verify_acl_rule(expected_sai_qualifiers, action="DROP") + dvs_acl.remove_acl_rule(PFCWD_TABLE_NAME, PFCWD_RULE_NAME_2) + + finally: + dvs_acl.remove_acl_table(PFCWD_TABLE_NAME) +# +# 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 From 99cfd5850b28216a24db7203dab2ca85f03cd774 Mon Sep 17 00:00:00 2001 From: Danny Allen Date: Mon, 22 Feb 2021 16:46:54 -0800 Subject: [PATCH 25/54] [acl] Enable VLAN ID qualifier for ACL rules (#1648) Signed-off-by: Danny Allen --- orchagent/aclorch.cpp | 19 +++++++++++++++++++ orchagent/aclorch.h | 1 + tests/mock_tests/aclorch_ut.cpp | 3 ++- tests/test_acl.py | 24 ++++++++++++++++++++++++ tests/test_acl_egress_table.py | 12 ++++++++++++ tests/test_mirror_ipv6_combined.py | 1 + tests/test_mirror_ipv6_separate.py | 2 ++ 7 files changed, 61 insertions(+), 1 deletion(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 2f14c61349..c24eeedb6d 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -32,6 +32,9 @@ extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch *gCrmOrch; +#define MIN_VLAN_ID 1 // 0 is a reserved VLAN ID +#define MAX_VLAN_ID 4095 // 4096 is a reserved VLAN ID + acl_rule_attr_lookup_t aclMatchLookup = { { MATCH_IN_PORTS, SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS }, @@ -43,6 +46,7 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_L4_SRC_PORT, SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT }, { MATCH_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT }, { MATCH_ETHER_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE }, + { MATCH_VLAN_ID, SAI_ACL_ENTRY_ATTR_FIELD_OUTER_VLAN_ID }, { MATCH_IP_PROTOCOL, SAI_ACL_ENTRY_ATTR_FIELD_IP_PROTOCOL }, { MATCH_NEXT_HEADER, SAI_ACL_ENTRY_ATTR_FIELD_IPV6_NEXT_HEADER }, { MATCH_TCP_FLAGS, SAI_ACL_ENTRY_ATTR_FIELD_TCP_FLAGS }, @@ -289,6 +293,17 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) value.aclfield.data.u16 = to_uint(attr_value); value.aclfield.mask.u16 = 0xFFFF; } + else if (attr_name == MATCH_VLAN_ID) + { + value.aclfield.data.u16 = to_uint(attr_value); + value.aclfield.mask.u16 = 0xFFF; + + if (value.aclfield.data.u16 < MIN_VLAN_ID || value.aclfield.data.u16 > MAX_VLAN_ID) + { + SWSS_LOG_ERROR("Invalid VLAN ID: %s", attr_value.c_str()); + return false; + } + } else if (attr_name == MATCH_DSCP) { /* Support both exact value match and value/mask match */ @@ -1415,6 +1430,10 @@ bool AclTable::create() table_attrs.push_back(attr); } + attr.id = SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID; + attr.value.booldata = true; + table_attrs.push_back(attr); + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE; attr.value.booldata = true; table_attrs.push_back(attr); diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 267fb6eeb9..d8eac7a95a 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -50,6 +50,7 @@ #define MATCH_ETHER_TYPE "ETHER_TYPE" #define MATCH_IP_PROTOCOL "IP_PROTOCOL" #define MATCH_NEXT_HEADER "NEXT_HEADER" +#define MATCH_VLAN_ID "VLAN_ID" #define MATCH_TCP_FLAGS "TCP_FLAGS" #define MATCH_IP_TYPE "IP_TYPE" #define MATCH_DSCP "DSCP" diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index bed031396e..cc6429ee1b 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -106,6 +106,7 @@ namespace aclorch_test auto v = vector( { { "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST", "2:SAI_ACL_BIND_POINT_TYPE_PORT,SAI_ACL_BIND_POINT_TYPE_LAG" }, { "SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE", "true" }, + { "SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID", "true" }, { "SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE", "true" }, { "SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL", "true" }, { "SAI_ACL_TABLE_ATTR_FIELD_SRC_IP", "true" }, @@ -420,8 +421,8 @@ namespace aclorch_test vector fields; fields.push_back({ "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST", "2:SAI_ACL_BIND_POINT_TYPE_PORT,SAI_ACL_BIND_POINT_TYPE_LAG" }); + fields.push_back({ "SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID", "true" }); fields.push_back({ "SAI_ACL_TABLE_ATTR_FIELD_ACL_IP_TYPE", "true" }); - fields.push_back({ "SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT", "true" }); fields.push_back({ "SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT", "true" }); fields.push_back({ "SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS", "true" }); diff --git a/tests/test_acl.py b/tests/test_acl.py index 93b08dfc25..51f4986079 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -135,6 +135,18 @@ def test_AclRuleOutPortsNonExistingInterface(self, dvs_acl, l3_acl_table): dvs_acl.verify_no_acl_rules() dvs_acl.remove_acl_rule(L3_TABLE_NAME, L3_RULE_NAME) + def test_AclRuleVlanId(self, dvs_acl, l3_acl_table): + config_qualifiers = {"VLAN_ID": "100"} + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_OUTER_VLAN_ID": dvs_acl.get_simple_qualifier_comparator("100&mask:0xfff") + } + + dvs_acl.create_acl_rule(L3_TABLE_NAME, L3_RULE_NAME, config_qualifiers) + dvs_acl.verify_acl_rule(expected_sai_qualifiers) + + dvs_acl.remove_acl_rule(L3_TABLE_NAME, L3_RULE_NAME) + dvs_acl.verify_no_acl_rules() + def test_V6AclTableCreationDeletion(self, dvs_acl): try: dvs_acl.create_acl_table(L3V6_TABLE_NAME, @@ -288,6 +300,18 @@ def test_V6AclRuleL4DstPortRange(self, dvs_acl, l3v6_acl_table): dvs_acl.remove_acl_rule(L3V6_TABLE_NAME, L3V6_RULE_NAME) dvs_acl.verify_no_acl_rules() + def test_V6AclRuleVlanId(self, dvs_acl, l3v6_acl_table): + config_qualifiers = {"VLAN_ID": "100"} + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_OUTER_VLAN_ID": dvs_acl.get_simple_qualifier_comparator("100&mask:0xfff") + } + + dvs_acl.create_acl_rule(L3V6_TABLE_NAME, L3V6_RULE_NAME, config_qualifiers) + dvs_acl.verify_acl_rule(expected_sai_qualifiers) + + dvs_acl.remove_acl_rule(L3V6_TABLE_NAME, L3V6_RULE_NAME) + dvs_acl.verify_no_acl_rules() + def test_InsertAclRuleBetweenPriorities(self, dvs_acl, l3_acl_table): rule_priorities = ["10", "20", "30", "40"] diff --git a/tests/test_acl_egress_table.py b/tests/test_acl_egress_table.py index baab97bbbf..f2b917ebc6 100644 --- a/tests/test_acl_egress_table.py +++ b/tests/test_acl_egress_table.py @@ -137,6 +137,18 @@ def test_EgressAclInnerL4DstPort(self, dvs_acl, egress_acl_table): dvs_acl.remove_acl_rule(TABLE_NAME, RULE_NAME) dvs_acl.verify_no_acl_rules() + def test_AclRuleVlanId(self, dvs_acl, egress_acl_table): + config_qualifiers = {"VLAN_ID": "100"} + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_FIELD_OUTER_VLAN_ID": dvs_acl.get_simple_qualifier_comparator("100&mask:0xfff") + } + + dvs_acl.create_acl_rule(TABLE_NAME, RULE_NAME, config_qualifiers, action="DROP", priority="1000") + dvs_acl.verify_acl_rule(expected_sai_qualifiers, action="DROP", priority="1000") + + dvs_acl.remove_acl_rule(TABLE_NAME, RULE_NAME) + dvs_acl.verify_no_acl_rules() + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying diff --git a/tests/test_mirror_ipv6_combined.py b/tests/test_mirror_ipv6_combined.py index 5ed0b72286..b2514f3ae9 100644 --- a/tests/test_mirror_ipv6_combined.py +++ b/tests/test_mirror_ipv6_combined.py @@ -172,6 +172,7 @@ def test_CombinedMirrorTableCreation(self, dvs, testlog): "SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS", "SAI_ACL_TABLE_ATTR_FIELD_DSCP", "SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE", + "SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID", "SAI_ACL_TABLE_ATTR_FIELD_IN_PORTS", "SAI_ACL_TABLE_ATTR_FIELD_SRC_IPV6", "SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6", diff --git a/tests/test_mirror_ipv6_separate.py b/tests/test_mirror_ipv6_separate.py index 839ac6bc09..50e40236a4 100644 --- a/tests/test_mirror_ipv6_separate.py +++ b/tests/test_mirror_ipv6_separate.py @@ -169,6 +169,7 @@ def test_MirrorTableCreation(self, dvs, testlog): "SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS", "SAI_ACL_TABLE_ATTR_FIELD_DSCP", "SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE", + "SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID", "SAI_ACL_TABLE_ATTR_FIELD_IN_PORTS", ] @@ -236,6 +237,7 @@ def test_MirrorV6TableCreation(self, dvs, testlog): "SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT", "SAI_ACL_TABLE_ATTR_FIELD_TCP_FLAGS", "SAI_ACL_TABLE_ATTR_FIELD_DSCP", + "SAI_ACL_TABLE_ATTR_FIELD_OUTER_VLAN_ID" ] expected_sai_list_attributes = [ From 1d3388100ef7edbc275746136d8a1db0769e652e Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Wed, 24 Feb 2021 14:24:35 -0800 Subject: [PATCH 26/54] [synchronous mode] Add failure notification for SAI failures in synchronous mode (#1596) Add function to notify users in the presence of SAI failures by throwing exceptions and trigger swss restart in synchronous mode. And include this function in routeorch and neighorch. This is a part of the first step in the SAI failure handling in orchagent with synchronous mode: 1. Failure notification mechanism to ensure enough notifications in the presence of SAI failures and avoid running switches with unhandled failures. 2. Add general failure handling logic by status. 3. Develop fine-grain failure handling mechanism for each orch to properly handle different SAI failures. This function aims to ensure enough notifications in the presence of SAI failures and avoid running switches with unhandled failures (on-par with asynchronous mode). --- cfgmgr/Makefile.am | 27 ++++++++------- orchagent/neighorch.cpp | 12 +++---- orchagent/orch.cpp | 77 +++++++++++++++++++++++++++++++++++++++++ orchagent/orch.h | 5 +++ orchagent/routeorch.cpp | 29 ++++++++-------- 5 files changed, 117 insertions(+), 33 deletions(-) diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 3321f82a4c..d1ea5978c1 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -2,6 +2,7 @@ INCLUDES = -I$(top_srcdir)/lib -I $(top_srcdir) -I $(top_srcdir)/orchagent -I $( CFLAGS_SAI = -I /usr/include/sai LIBNL_CFLAGS = -I/usr/include/libnl3 LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 +SAIMETA_LIBS = -lsaimeta -lsaimetadata bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd @@ -24,64 +25,64 @@ endif vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vlanmgrd_LDADD = -lswsscommon +vlanmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) teammgrd_SOURCES = teammgrd.cpp teammgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h teammgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) teammgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -teammgrd_LDADD = -lswsscommon +teammgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) portmgrd_SOURCES = portmgrd.cpp portmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -portmgrd_LDADD = -lswsscommon +portmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -intfmgrd_LDADD = -lswsscommon +intfmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) buffermgrd_SOURCES = buffermgrd.cpp buffermgr.cpp buffermgrdyn.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h buffermgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) buffermgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -buffermgrd_LDADD = -lswsscommon +buffermgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) vrfmgrd_SOURCES = vrfmgrd.cpp vrfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vrfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vrfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vrfmgrd_LDADD = -lswsscommon +vrfmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) nbrmgrd_SOURCES = nbrmgrd.cpp nbrmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h nbrmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CFLAGS) nbrmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CPPFLAGS) -nbrmgrd_LDADD = -lswsscommon $(LIBNL_LIBS) +nbrmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) $(LIBNL_LIBS) vxlanmgrd_SOURCES = vxlanmgrd.cpp vxlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vxlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vxlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vxlanmgrd_LDADD = -lswsscommon +vxlanmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) sflowmgrd_SOURCES = sflowmgrd.cpp sflowmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -sflowmgrd_LDADD = -lswsscommon +sflowmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) natmgrd_SOURCES = natmgrd.cpp natmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -natmgrd_LDADD = -lswsscommon +natmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) coppmgrd_SOURCES = coppmgrd.cpp coppmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h coppmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) coppmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -coppmgrd_LDADD = -lswsscommon +coppmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) tunnelmgrd_SOURCES = tunnelmgrd.cpp tunnelmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h tunnelmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) tunnelmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -tunnelmgrd_LDADD = -lswsscommon +tunnelmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) macsecmgrd_SOURCES = macsecmgrd.cpp macsecmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -macsecmgrd_LDADD = -lswsscommon +macsecmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index c850bf0ee6..cc3ee64401 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -204,7 +204,7 @@ bool NeighOrch::addNextHop(const IpAddress &ipAddress, const string &alias) { SWSS_LOG_ERROR("Failed to create next hop %s on %s, rv:%d", ipAddress.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiCreateStatus(SAI_API_NEXT_HOP, status); } SWSS_LOG_NOTICE("Created next hop %s on %s", @@ -678,7 +678,7 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress { SWSS_LOG_ERROR("Failed to create neighbor %s on %s, rv:%d", macAddress.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiCreateStatus(SAI_API_NEIGHBOR, status); } } SWSS_LOG_NOTICE("Created neighbor ip %s, %s on %s", ip_address.to_string().c_str(), @@ -701,7 +701,7 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress { SWSS_LOG_ERROR("Failed to remove neighbor %s on %s, rv:%d", macAddress.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiRemoveStatus(SAI_API_NEIGHBOR, status); } m_intfsOrch->decreaseRouterIntfsRefCount(alias); @@ -725,7 +725,7 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress { SWSS_LOG_ERROR("Failed to update neighbor %s on %s, rv:%d", macAddress.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiSetStatus(SAI_API_NEIGHBOR, status); } SWSS_LOG_NOTICE("Updated neighbor %s on %s", macAddress.to_string().c_str(), alias.c_str()); } @@ -798,7 +798,7 @@ bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry, bool disable) { SWSS_LOG_ERROR("Failed to remove next hop %s on %s, rv:%d", ip_address.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiRemoveStatus(SAI_API_NEXT_HOP, status); } } @@ -830,7 +830,7 @@ bool NeighOrch::removeNeighbor(const NeighborEntry &neighborEntry, bool disable) { SWSS_LOG_ERROR("Failed to remove neighbor %s on %s, rv:%d", m_syncdNeighbors[neighborEntry].mac.to_string().c_str(), alias.c_str(), status); - return false; + return handleSaiRemoveStatus(SAI_API_NEIGHBOR, status); } } diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index ea3e8e78b2..6de6f189f7 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -10,6 +10,7 @@ #include "tokenize.h" #include "logger.h" #include "consumerstatetable.h" +#include "sai_serialize.h" using namespace swss; @@ -685,6 +686,82 @@ Executor *Orch::getExecutor(string executorName) return NULL; } +bool Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context) +{ + /* + * This function aims to provide coarse handling of failures in sairedis create + * operation (i.e., notify users by throwing excepions when failures happen). + * Return value: true - Handled the status successfully. No need to retry this SAI operation. + * false - Cannot handle the status. Need to retry the SAI operation. + * TODO: 1. Add general handling logic for specific statuses (e.g., SAI_STATUS_ITEM_ALREADY_EXISTS) + * 2. Develop fine-grain failure handling mechanisms and replace this coarse handling + * in each orch. + * 3. Take the type of sai api into consideration. + */ + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return true; + default: + SWSS_LOG_ERROR("Encountered failure in create operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + exit(EXIT_FAILURE); + } + return false; +} + +bool Orch::handleSaiSetStatus(sai_api_t api, sai_status_t status, void *context) +{ + /* + * This function aims to provide coarse handling of failures in sairedis set + * operation (i.e., notify users by throwing excepions when failures happen). + * Return value: true - Handled the status successfully. No need to retry this SAI operation. + * false - Cannot handle the status. Need to retry the SAI operation. + * TODO: 1. Add general handling logic for specific statuses + * 2. Develop fine-grain failure handling mechanisms and replace this coarse handling + * in each orch. + * 3. Take the type of sai api into consideration. + */ + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiSetStatus"); + return true; + default: + SWSS_LOG_ERROR("Encountered failure in set operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + exit(EXIT_FAILURE); + } + return false; +} + +bool Orch::handleSaiRemoveStatus(sai_api_t api, sai_status_t status, void *context) +{ + /* + * This function aims to provide coarse handling of failures in sairedis remove + * operation (i.e., notify users by throwing excepions when failures happen). + * Return value: true - Handled the status successfully. No need to retry this SAI operation. + * false - Cannot handle the status. Need to retry the SAI operation. + * TODO: 1. Add general handling logic for specific statuses (e.g., SAI_STATUS_OBJECT_IN_USE, + * SAI_STATUS_ITEM_NOT_FOUND) + * 2. Develop fine-grain failure handling mechanisms and replace this coarse handling + * in each orch. + * 3. Take the type of sai api into consideration. + */ + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiRemoveStatus"); + return true; + default: + SWSS_LOG_ERROR("Encountered failure in remove operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + exit(EXIT_FAILURE); + } + return false; +} + void Orch2::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); diff --git a/orchagent/orch.h b/orchagent/orch.h index 1a7197dfa9..fcecf98a22 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -235,6 +235,11 @@ class Orch /* Note: consumer will be owned by this class */ void addExecutor(Executor* executor); Executor *getExecutor(std::string executorName); + + /* Handling SAI status*/ + virtual bool handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context = nullptr); + virtual bool handleSaiSetStatus(sai_api_t api, sai_status_t status, void *context = nullptr); + virtual bool handleSaiRemoveStatus(sai_api_t api, sai_status_t status, void *context = nullptr); private: void removeMeFromObjsReferencedByMe(type_map &type_maps, const std::string &table, const std::string &obj_name, const std::string &field, const std::string &old_referenced_obj_name); void addConsumer(swss::DBConnector *db, std::string tableName, int pri = default_orch_pri); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 3a88b5793c..641f81589d 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -331,7 +331,7 @@ bool RouteOrch::validnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t& { SWSS_LOG_ERROR("Failed to add next hop member to group %" PRIx64 ": %d\n", nhopgroup->second.next_hop_group_id, status); - return false; + return handleSaiCreateStatus(SAI_API_NEXT_HOP_GROUP, status); } ++count; @@ -371,7 +371,7 @@ bool RouteOrch::invalidnexthopinNextHopGroup(const NextHopKey &nexthop, uint32_t { SWSS_LOG_ERROR("Failed to remove next hop member %" PRIx64 " from group %" PRIx64 ": %d\n", nexthop_id, nhopgroup->second.next_hop_group_id, status); - return false; + return handleSaiRemoveStatus(SAI_API_NEXT_HOP_GROUP, status); } ++count; @@ -950,7 +950,7 @@ bool RouteOrch::createFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create next hop group rv:%d", status); - return false; + return handleSaiCreateStatus(SAI_API_NEXT_HOP_GROUP, status); } gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); @@ -968,7 +968,7 @@ bool RouteOrch::removeFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id { SWSS_LOG_ERROR("Failed to remove next hop group %" PRIx64 ", rv:%d", next_hop_group_id, status); - return false; + return handleSaiRemoveStatus(SAI_API_NEXT_HOP_GROUP, status); } gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); @@ -1033,7 +1033,7 @@ bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) { SWSS_LOG_ERROR("Failed to create next hop group %s, rv:%d", nexthops.to_string().c_str(), status); - return false; + return handleSaiCreateStatus(SAI_API_NEXT_HOP_GROUP, status); } m_nextHopGroupCount ++; @@ -1150,7 +1150,7 @@ bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) { SWSS_LOG_ERROR("Failed to remove next hop group member[%zu] %" PRIx64 ", rv:%d", i, next_hop_ids[i], statuses[i]); - return false; + return handleSaiRemoveStatus(SAI_API_NEXT_HOP_GROUP, statuses[i]); } gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); @@ -1160,7 +1160,7 @@ bool RouteOrch::removeNextHopGroup(const NextHopGroupKey &nexthops) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to remove next hop group %" PRIx64 ", rv:%d", next_hop_group_id, status); - return false; + return handleSaiRemoveStatus(SAI_API_NEXT_HOP_GROUP, status); } m_nextHopGroupCount --; @@ -1626,7 +1626,8 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey } else if (it_route == m_syncdRoutes.at(vrf_id).end()) { - if (*it_status++ != SAI_STATUS_SUCCESS) + sai_status_t status = *it_status++; + if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create route %s with next hop(s) %s", ipPrefix.to_string().c_str(), nextHops.to_string().c_str()); @@ -1635,7 +1636,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { removeNextHopGroup(nextHops); } - return false; + return handleSaiCreateStatus(SAI_API_ROUTE, status); } if (ipPrefix.isV4()) @@ -1665,7 +1666,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { SWSS_LOG_ERROR("Failed to set route %s with packet action forward, %d", ipPrefix.to_string().c_str(), status); - return false; + return handleSaiSetStatus(SAI_API_ROUTE, status); } } @@ -1674,7 +1675,7 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey { SWSS_LOG_ERROR("Failed to set route %s with next hop(s) %s", ipPrefix.to_string().c_str(), nextHops.to_string().c_str()); - return false; + return handleSaiSetStatus(SAI_API_ROUTE, status); } /* Increase the ref_count for the next hop (group) entry */ @@ -1792,7 +1793,7 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) { SWSS_LOG_ERROR("Failed to set route %s packet action to drop, rv:%d", ipPrefix.to_string().c_str(), status); - return false; + return handleSaiSetStatus(SAI_API_ROUTE, status); } SWSS_LOG_INFO("Set route %s packet action to drop", ipPrefix.to_string().c_str()); @@ -1802,7 +1803,7 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) { SWSS_LOG_ERROR("Failed to set route %s next hop ID to NULL, rv:%d", ipPrefix.to_string().c_str(), status); - return false; + return handleSaiSetStatus(SAI_API_ROUTE, status); } SWSS_LOG_INFO("Set route %s next hop ID to NULL", ipPrefix.to_string().c_str()); @@ -1813,7 +1814,7 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to remove route prefix:%s\n", ipPrefix.to_string().c_str()); - return false; + return handleSaiRemoveStatus(SAI_API_ROUTE, status); } if (ipPrefix.isV4()) From 721f47d9959b6cc27bb99d6b8d6e5ee2267b4151 Mon Sep 17 00:00:00 2001 From: nkelapur Date: Wed, 3 Mar 2021 08:16:42 +0530 Subject: [PATCH 27/54] Added changes to handle dependency check in FdbSyncd and FpmSyncd for warm-boot (#1556) Added changes to handle dependency check in FpmSyncd and FdbSyncd for warmreboot. This was done to ensure for EVPN warm-reboot the order of data replay to kernel is maintained across various submodules and the kernel programming will be successful. --- fdbsyncd/fdbsync.cpp | 39 +++++++++++++++++++-- fdbsyncd/fdbsync.h | 15 ++++++-- fdbsyncd/fdbsyncd.cpp | 57 +++++++++++++++++++++++++++++-- fpmsyncd/fpmsyncd.cpp | 7 ++++ tests/test_warm_reboot.py | 4 +-- warmrestart/warmRestartAssist.cpp | 17 +++++++++ warmrestart/warmRestartAssist.h | 3 ++ 7 files changed, 132 insertions(+), 10 deletions(-) diff --git a/fdbsyncd/fdbsync.cpp b/fdbsyncd/fdbsync.cpp index 9a88e557bf..30c9e2d54c 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -43,6 +43,36 @@ FdbSync::~FdbSync() } } + +// Check if interface entries are restored in kernel +bool FdbSync::isIntfRestoreDone() +{ + vector required_modules = { + "vxlanmgrd", + "intfmgrd", + "vlanmgrd", + "vrfmgrd" + }; + + for (string& module : required_modules) + { + WarmStart::WarmStartState state; + + WarmStart::getWarmStartState(module, state); + if (state == WarmStart::REPLAYED || state == WarmStart::RECONCILED) + { + SWSS_LOG_INFO("Module %s Replayed or Reconciled %d",module.c_str(), (int) state); + } + else + { + SWSS_LOG_INFO("Module %s NOT Replayed or Reconciled %d",module.c_str(), (int) state); + return false; + } + } + + return true; +} + void FdbSync::processCfgEvpnNvo() { std::deque entries; @@ -447,6 +477,10 @@ void FdbSync::macDelVxlanDB(string key) fvVector.push_back(t); fvVector.push_back(v); + SWSS_LOG_NOTICE("%sVXLAN_FDB_TABLE: DEL_KEY %s vtep:%s type:%s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , + key.c_str(), vtep.c_str(), type.c_str()); + // If warmstart is in progress, we take all netlink changes into the cache map if (m_AppRestartAssist->isWarmStartInProgress()) { @@ -454,7 +488,6 @@ void FdbSync::macDelVxlanDB(string key) return; } - SWSS_LOG_INFO("VXLAN_FDB_TABLE: DEL_KEY %s vtep:%s type:%s", key.c_str(), vtep.c_str(), type.c_str()); m_fdbTable.del(key); return; @@ -476,6 +509,9 @@ void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t fvVector.push_back(t); fvVector.push_back(v); + SWSS_LOG_INFO("%sVXLAN_FDB_TABLE: ADD_KEY %s vtep:%s type:%s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , + key.c_str(), svtep.c_str(), type.c_str()); // If warmstart is in progress, we take all netlink changes into the cache map if (m_AppRestartAssist->isWarmStartInProgress()) { @@ -483,7 +519,6 @@ void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t return; } - SWSS_LOG_INFO("VXLAN_FDB_TABLE: ADD_KEY %s vtep:%s type:%s", key.c_str(), svtep.c_str(), type.c_str()); m_fdbTable.set(key, fvVector); return; diff --git a/fdbsyncd/fdbsync.h b/fdbsyncd/fdbsync.h index c8248ffefb..ee6aa0845b 100644 --- a/fdbsyncd/fdbsync.h +++ b/fdbsyncd/fdbsync.h @@ -9,8 +9,17 @@ #include "netmsg.h" #include "warmRestartAssist.h" -// The timeout value (in seconds) for fdbsyncd reconcilation logic -#define DEFAULT_FDBSYNC_WARMSTART_TIMER 30 +/* + * Default timer interval for fdbsyncd reconcillation + */ +#define DEFAULT_FDBSYNC_WARMSTART_TIMER 120 + +/* + * This is the MAX time in seconds, fdbsyncd will wait after warm-reboot + * for the interface entries to be recreated in kernel before attempting to + * write the FDB data to kernel + */ +#define INTF_RESTORE_MAX_WAIT_TIME 180 namespace swss { @@ -43,7 +52,7 @@ class FdbSync : public NetMsg virtual void onMsg(int nlmsg_type, struct nl_object *obj); - bool isFdbRestoreDone(); + bool isIntfRestoreDone(); AppRestartAssist *getRestartAssist() { diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index 8d82db1829..dac4bb85e5 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -7,6 +7,7 @@ #include "netdispatcher.h" #include "netlink.h" #include "fdbsyncd/fdbsync.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -35,6 +36,7 @@ int main(int argc, char **argv) Selectable *temps; int ret; Select s; + SelectableTimer replayCheckTimer(timespec{0, 0}); using namespace std::chrono; @@ -45,7 +47,29 @@ int main(int argc, char **argv) if (sync.getRestartAssist()->isWarmStartInProgress()) { sync.getRestartAssist()->readTablesToMap(); - SWSS_LOG_NOTICE("Starting ReconcileTimer"); + + steady_clock::time_point starttime = steady_clock::now(); + while (!sync.isIntfRestoreDone()) + { + duration time_span = + duration_cast>(steady_clock::now() - starttime); + int pasttime = int(time_span.count()); + + if (pasttime > INTF_RESTORE_MAX_WAIT_TIME) + { + SWSS_LOG_INFO("timed-out before all interface data was replayed to kernel!!!"); + throw runtime_error("fdbsyncd: timedout on interface data replay"); + } + sleep(1); + } + replayCheckTimer.setInterval(timespec{1, 0}); + replayCheckTimer.start(); + s.addSelectable(&replayCheckTimer); + } + else + { + sync.getRestartAssist()->warmStartDisabled(); + sync.m_reconcileDone = true; } netlink.registerGroup(RTNLGRP_LINK); @@ -67,7 +91,7 @@ int main(int argc, char **argv) { s.select(&temps); - if(temps == (Selectable *)sync.getFdbStateTable()) + if (temps == (Selectable *)sync.getFdbStateTable()) { sync.processStateFdb(); } @@ -75,6 +99,33 @@ int main(int argc, char **argv) { sync.processCfgEvpnNvo(); } + else if (temps == &replayCheckTimer) + { + if (sync.getFdbStateTable()->empty() && sync.getCfgEvpnNvoTable()->empty()) + { + sync.getRestartAssist()->appDataReplayed(); + SWSS_LOG_NOTICE("FDB Replay Complete"); + s.removeSelectable(&replayCheckTimer); + + /* Obtain warm-restart timer defined for routing application */ + uint32_t warmRestartIval = WarmStart::getWarmStartTimer("bgp","bgp"); + if (warmRestartIval) + { + sync.getRestartAssist()->setReconcileInterval(warmRestartIval); + } + //Else the interval is already set to default value + + //TODO: Optimise the reconcillation time using eoiu - issue#1657 + SWSS_LOG_NOTICE("Starting ReconcileTimer"); + sync.getRestartAssist()->startReconcileTimer(s); + } + else + { + replayCheckTimer.setInterval(timespec{1, 0}); + // re-start replay check timer + replayCheckTimer.start(); + } + } else { /* @@ -88,7 +139,7 @@ int main(int argc, char **argv) sync.m_reconcileDone = true; sync.getRestartAssist()->stopReconcileTimer(s); sync.getRestartAssist()->reconcile(); - SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete (Timer)"); + SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete"); } } } diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index 2c20e3d8bc..8f797e178c 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -18,6 +18,7 @@ using namespace swss; */ const uint32_t DEFAULT_ROUTING_RESTART_INTERVAL = 120; + // Wait 3 seconds after detecting EOIU reached state // TODO: support eoiu hold interval config const uint32_t DEFAULT_EOIU_HOLD_INTERVAL = 3; @@ -67,6 +68,7 @@ int main(int argc, char **argv) SelectableTimer eoiuCheckTimer(timespec{0, 0}); // After eoiu flags are detected, start a hold timer before starting reconciliation. SelectableTimer eoiuHoldTimer(timespec{0, 0}); + /* * Pipeline should be flushed right away to deal with state pending * from previous try/catch iterations. @@ -108,6 +110,10 @@ int main(int argc, char **argv) s.addSelectable(&eoiuCheckTimer); SWSS_LOG_NOTICE("Warm-Restart eoiuCheckTimer timer started."); } + else + { + sync.m_warmStartHelper.setState(WarmStart::WSDISABLED); + } while (true) { @@ -132,6 +138,7 @@ int main(int argc, char **argv) { SWSS_LOG_NOTICE("Warm-Restart EOIU hold timer expired."); } + if (sync.m_warmStartHelper.inProgress()) { sync.m_warmStartHelper.reconcile(); diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index a7a567fb6f..84f2e57b87 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -76,7 +76,7 @@ def swss_app_check_RestoreCount_single(state_db, restore_count, name): if fv[0] == "restore_count": assert int(fv[1]) == restore_count[key] + 1 elif fv[0] == "state": - assert fv[1] == "reconciled" or fv[1] == "disabled" + assert fv[1] == "reconciled" or fv[1] == "disabled" def swss_app_check_warmstart_state(state_db, name, state): warmtbl = swsscommon.Table(state_db, swsscommon.STATE_WARM_RESTART_TABLE_NAME) @@ -1150,7 +1150,7 @@ def test_routing_WarmRestart(self, dvs, testlog): time.sleep(5) # Verify FSM - swss_app_check_warmstart_state(state_db, "bgp", "") + swss_app_check_warmstart_state(state_db, "bgp", "disabled") # Verify that multiple changes are seen in swss and sairedis logs as there's # no warm-reboot logic in place. diff --git a/warmrestart/warmRestartAssist.cpp b/warmrestart/warmRestartAssist.cpp index 3bb351fcb7..988f8279db 100644 --- a/warmrestart/warmRestartAssist.cpp +++ b/warmrestart/warmRestartAssist.cpp @@ -117,6 +117,16 @@ AppRestartAssist::cache_state_t AppRestartAssist::getCacheEntryState(const std:: throw std::logic_error("cache entry state is invalid"); } +void AppRestartAssist::appDataReplayed() +{ + WarmStart::setWarmStartState(m_appName, WarmStart::REPLAYED); +} + +void AppRestartAssist::warmStartDisabled() +{ + WarmStart::setWarmStartState(m_appName, WarmStart::WSDISABLED); +} + // Read table(s) from APPDB and append stale flag then insert to cachemap void AppRestartAssist::readTablesToMap() { @@ -274,6 +284,13 @@ void AppRestartAssist::reconcile() return; } +// set the reconcile interval +void AppRestartAssist::setReconcileInterval(uint32_t time) +{ + m_reconcileTimer = time; + m_warmStartTimer.setInterval(timespec{m_reconcileTimer, 0}); +} + // start the timer, take Select class "s" to add the timer. void AppRestartAssist::startReconcileTimer(Select &s) { diff --git a/warmrestart/warmRestartAssist.h b/warmrestart/warmRestartAssist.h index 83ea74fa54..68fc4c278f 100644 --- a/warmrestart/warmRestartAssist.h +++ b/warmrestart/warmRestartAssist.h @@ -75,10 +75,13 @@ class AppRestartAssist DELETE = 3 }; // These functions were used as described in the class description + void setReconcileInterval(uint32_t time); void startReconcileTimer(Select &s); void stopReconcileTimer(Select &s); bool checkReconcileTimer(Selectable *s); void readTablesToMap(void); + void appDataReplayed(void); + void warmStartDisabled(void); void insertToMap(std::string tableName, std::string key, std::vector fvVector, bool delete_key); void reconcile(void); bool isWarmStartInProgress(void) From a02d8881a0984e85f488df54e5253f589f828984 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Thu, 4 Mar 2021 10:02:44 -0800 Subject: [PATCH 28/54] Update StateDB with error if state change failed, Update APP_DB in all state chg req (#1662) If orchagent fails in state change, update StateDB with "error" state Update APP_DB for every state change request from Linkmgr irrespective of internal handling in orchagent --- orchagent/muxorch.cpp | 19 ++++++++++++++----- orchagent/muxorch.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 61ee046f19..8e581a11ca 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -43,7 +43,7 @@ extern sai_router_interface_api_t* sai_router_intfs_api; #define MUX_ACL_TABLE_NAME "mux_acl_table"; #define MUX_ACL_RULE_NAME "mux_acl_rule"; #define MUX_HW_STATE_UNKNOWN "unknown" -#define MUX_HW_STATE_PENDING "pending" +#define MUX_HW_STATE_ERROR "error" const map, MuxStateChange> muxStateTransition = { @@ -387,6 +387,9 @@ void MuxCable::setState(string new_state) SWSS_LOG_NOTICE("[%s] Set MUX state from %s to %s", mux_name_.c_str(), muxStateValToString.at(state_).c_str(), new_state.c_str()); + // Update HW Mux cable state anyways + mux_cb_orch_->updateMuxState(mux_name_, new_state); + MuxState ns = muxStateStringToVal.at(new_state); auto it = muxStateTransition.find(make_pair(state_, ns)); @@ -398,8 +401,6 @@ void MuxCable::setState(string new_state) return; } - mux_cb_orch_->updateMuxState(mux_name_, new_state); - MuxState state = state_; state_ = ns; @@ -410,6 +411,7 @@ void MuxCable::setState(string new_state) //Reset back to original state state_ = state; st_chg_in_progress_ = false; + st_chg_failed_ = true; throw std::runtime_error("Failed to handle state transition"); } @@ -1272,7 +1274,7 @@ bool MuxCableOrch::addOperation(const Request& request) { SWSS_LOG_ERROR("Mux Error setting state %s for port %s. Error: %s", state.c_str(), port_name.c_str(), error.what()); - return false; + return true; } SWSS_LOG_NOTICE("Mux State set to %s for port %s", state.c_str(), port_name.c_str()); @@ -1341,7 +1343,14 @@ bool MuxStateOrch::addOperation(const Request& request) if (mux_state != hw_state) { - mux_state = MUX_HW_STATE_UNKNOWN; + if (mux_obj->isStateChangeFailed()) + { + mux_state = MUX_HW_STATE_ERROR; + } + else + { + mux_state = MUX_HW_STATE_UNKNOWN; + } } SWSS_LOG_NOTICE("Mux setting State DB entry (hw state %s, mux state %s) for port %s", diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index f52343f22c..3264e4967a 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -87,6 +87,7 @@ class MuxCable void setState(string state); string getState(); bool isStateChangeInProgress() { return st_chg_in_progress_; } + bool isStateChangeFailed() { return st_chg_failed_; } bool isIpInSubnet(IpAddress ip); void updateNeighbor(NextHopKey nh, bool add); @@ -107,6 +108,7 @@ class MuxCable MuxState state_ = MuxState::MUX_STATE_INIT; bool st_chg_in_progress_ = false; + bool st_chg_failed_ = false; IpPrefix srv_ip4_, srv_ip6_; IpAddress peer_ip4_; From c2cbeb55e23b0702c5f47d433ad6dc330471a9ff Mon Sep 17 00:00:00 2001 From: Wenda Ni Date: Thu, 4 Mar 2021 20:43:23 -0800 Subject: [PATCH 29/54] [ci]: Purge swss before install (#1654) Observe persistent failure of newly added vs test on testing code change in intfmgrd after migrating to azure pipeline. #1521 passed in legacy Jenkins pipeline. What we found is that the code change in the swss compiled with PR change is not properly installed in vs docker for a direct installation using dpkg -i. This is confirmed by verifying pipeline artifacts that md5sum value of /user/bin/intfmgrd changes if we install swss deb aritfact into the docker vs artifact, while that of /usr/bin/orchangent stays unchanged. md5sum before swss install in vs docker # md5sum /usr/bin/orchagent 28307a7805ea6f3bc5057c0257bf46e6 /usr/bin/orchagent # md5sum /usr/bin/intfmgrd fa2b06e20be683286adb47c55635a86d /usr/bin/intfmgrd md5sum after swss install # dpkg -i swss_1.0.0_amd64.deb (Reading database ... 19180 files and directories currently installed.) Preparing to unpack swss_1.0.0_amd64.deb ... Unpacking swss (1.0.0) over (1.0.0) ... Setting up swss (1.0.0) ... # md5sum /usr/bin/orchagent 28307a7805ea6f3bc5057c0257bf46e6 /usr/bin/orchagent # md5sum /usr/bin/intfmgrd e959340709e7aedd7489e69dfd19768f /usr/bin/intfmgrd Signed-off-by: Wenda Ni --- .azure-pipelines/docker-sonic-vs/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure-pipelines/docker-sonic-vs/Dockerfile b/.azure-pipelines/docker-sonic-vs/Dockerfile index f2bfb425b9..d425a6ffea 100644 --- a/.azure-pipelines/docker-sonic-vs/Dockerfile +++ b/.azure-pipelines/docker-sonic-vs/Dockerfile @@ -13,4 +13,5 @@ RUN dpkg -i /debs/libsairedis_1.0.0_amd64.deb RUN dpkg -i /debs/libsaivs_1.0.0_amd64.deb RUN dpkg -i /debs/syncd-vs_1.0.0_amd64.deb +RUN dpkg --purge swss RUN dpkg -i /debs/swss_1.0.0_amd64.deb From b6db9ddd6ded7463fbe91b2e4c7d5ae8a801b167 Mon Sep 17 00:00:00 2001 From: Peter Yu Date: Fri, 5 Mar 2021 22:59:35 +0800 Subject: [PATCH 30/54] [intfsorch] Create subport with the entry contains necessary attributes (#1650) Subport mtu and admin_status initialized to values of 0 and false resepectively. --- orchagent/intfsorch.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 649293c151..783f965f3c 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -588,8 +588,8 @@ void IntfsOrch::doTask(Consumer &consumer) string vrf_name = "", vnet_name = "", nat_zone = ""; MacAddress mac; - uint32_t mtu; - bool adminUp; + uint32_t mtu = 0; + bool adminUp = false; uint32_t nat_zone_id = 0; string proxy_arp = ""; string inband_type = ""; @@ -742,7 +742,7 @@ void IntfsOrch::doTask(Consumer &consumer) Port port; if (!gPortsOrch->getPort(alias, port)) { - if (isSubIntf) + if (!ip_prefix_in_key && isSubIntf) { if (!gPortsOrch->addSubPort(port, alias, adminUp, mtu)) { From 0cce6db29eaee09a577049385677b121fff30131 Mon Sep 17 00:00:00 2001 From: Dmytro Shevchuk <68949102+dmytroxshevchuk@users.noreply.github.com> Date: Fri, 5 Mar 2021 20:09:05 +0200 Subject: [PATCH 31/54] [portorch] parse on/off value from autoneg (#1658) Changed parsing autoneg from 1/0 to on/off --- orchagent/portsorch.cpp | 2 +- tests/test_port_an.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 89d8c53802..57b3df9555 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2228,7 +2228,7 @@ void PortsOrch::doPortTask(Consumer &consumer) /* Set autoneg and ignore the port speed setting */ else if (fvField(i) == "autoneg") { - an = (int)stoul(fvValue(i)); + an = (fvValue(i) == "on"); } /* Set port serdes Pre-emphasis */ else if (fvField(i) == "preemphasis") diff --git a/tests/test_port_an.py b/tests/test_port_an.py index 16e7e04dba..233b5fa02b 100644 --- a/tests/test_port_an.py +++ b/tests/test_port_an.py @@ -12,8 +12,8 @@ def test_PortAutoNegCold(self, dvs, testlog): tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") - # set autoneg = false and speed = 1000 - fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) + # set autoneg = true and speed = 1000 + fvs = swsscommon.FieldValuePairs([("autoneg","on"), ("speed", "1000")]) tbl.set("Ethernet0", fvs) @@ -50,7 +50,7 @@ def test_PortAutoNegCold(self, dvs, testlog): assert fv[1] == "1:100" # change autoneg to false - fvs = swsscommon.FieldValuePairs([("autoneg","0")]) + fvs = swsscommon.FieldValuePairs([("autoneg","off")]) tbl.set("Ethernet0", fvs) @@ -99,7 +99,7 @@ def test_PortAutoNegWarm(self, dvs, testlog): stbl = swsscommon.Table(sdb, "PORT_TABLE") # set autoneg = true and speed = 1000 - fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) + fvs = swsscommon.FieldValuePairs([("autoneg","on"), ("speed", "1000")]) ctbl.set("Ethernet0", fvs) time.sleep(1) From 0fa4d7438ee805a16e486f3fb55e65d5b7de2fa1 Mon Sep 17 00:00:00 2001 From: vganesan-nokia <67648637+vganesan-nokia@users.noreply.github.com> Date: Fri, 5 Mar 2021 14:12:45 -0500 Subject: [PATCH 32/54] [linksync] Netdev oper status determination using IFF_RUNNING (#1568) The objective of this change is to make orchagent/linksync align with FRR/Zebra in handling interface states. Zebra uses IFF_RUNNING flag to determine interfaces' oper state while linksync uses IFF_LOWER_UP. Zebra rightly depends on IFF_RUNNING flag. As a routing daemon, zebra wants to know whether the interface is capable of passing packets or not. The flag IFF_RUNNING indicates exactly that (based on comment in if.h, this flag reflects the interface oper up state as defined in RFC2863). Since orchagent uses IFF_LOWER_UP, which comes earlier than IFF_RUNNING, there is interface state mismatch between zebra and orchagent for a window of time. Since with voq implementation we write static neigh/routes when the netdev (inband port) becomes oper up and bgp depends on these kernel entries, there is a need for this change to have the interface state timing same in both orchagent and zebra. Refer issue Azure/sonic-buildimage#6295 for detailed information. Signed-off-by: vedganes --- portsyncd/linksync.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index 1985559e56..820da0ce60 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -174,7 +174,7 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) unsigned int flags = rtnl_link_get_flags(link); bool admin = flags & IFF_UP; - bool oper = flags & IFF_LOWER_UP; + bool oper = flags & IFF_RUNNING; char addrStr[MAX_ADDR_SIZE+1] = {0}; nl_addr2str(rtnl_link_get_addr(link), addrStr, MAX_ADDR_SIZE); From 03a0e21807c4998424e4a1864e6aaf651fa8cb70 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Sun, 7 Mar 2021 12:17:09 -0800 Subject: [PATCH 33/54] [mux] VS test for neigh, route and fdb (#1656) VS test to cover: IPv4 and V6 neighbor add and simulate mux state change. Route add and simulate mux state change. ECMP route add and simulate mux state change. Neighbor handing based on FDB learnt. --- tests/test_mux.py | 405 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 343 insertions(+), 62 deletions(-) diff --git a/tests/test_mux.py b/tests/test_mux.py index 659c686e19..c1eaae75ed 100644 --- a/tests/test_mux.py +++ b/tests/test_mux.py @@ -1,5 +1,6 @@ import time import pytest +import json from swsscommon import swsscommon @@ -7,23 +8,28 @@ def create_fvs(**kwargs): return swsscommon.FieldValuePairs(list(kwargs.items())) -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - time.sleep(1) - -def create_entry_tbl(db, table, separator, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - +tunnel_nh_id = 0 class TestMuxTunnelBase(object): + APP_MUX_CABLE = "MUX_CABLE_TABLE" APP_TUNNEL_DECAP_TABLE_NAME = "TUNNEL_DECAP_TABLE" ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" ASIC_TUNNEL_TERM_ENTRIES = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" - + ASIC_NEIGH_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY" + ASIC_NEXTHOP_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" + ASIC_ROUTE_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + CONFIG_MUX_CABLE = "MUX_CABLE" + + SERV1_IPV4 = "192.168.0.100" + SERV1_IPV6 = "fc02:1000::100" + SERV2_IPV4 = "192.168.0.101" + SERV2_IPV6 = "fc02:1000::101" + IPV4_MASK = "/32" + IPV6_MASK = "/128" + TUNNEL_NH_ID = 0 + ecn_modes_map = { "standard" : "SAI_TUNNEL_DECAP_ECN_MODE_STANDARD", "copy_from_outer": "SAI_TUNNEL_DECAP_ECN_MODE_COPY_FROM_OUTER" @@ -40,45 +46,299 @@ class TestMuxTunnelBase(object): } + def create_vlan_interface(self, confdb, asicdb, dvs): + + fvs = {"vlanid": "1000"} + confdb.create_entry("VLAN", "Vlan1000", fvs) + + fvs = {"tagging_mode": "untagged"} + confdb.create_entry("VLAN_MEMBER", "Vlan1000|Ethernet0", fvs) + confdb.create_entry("VLAN_MEMBER", "Vlan1000|Ethernet4", fvs) + + fvs = {"NULL": "NULL"} + confdb.create_entry("VLAN_INTERFACE", "Vlan1000", fvs) + confdb.create_entry("VLAN_INTERFACE", "Vlan1000|192.168.0.1/24", fvs) + + dvs.runcmd("config interface startup Ethernet0") + dvs.runcmd("config interface startup Ethernet4") + + + def create_mux_cable(self, confdb): + + fvs = { "server_ipv4":self.SERV1_IPV4+self.IPV4_MASK, "server_ipv6":self.SERV1_IPV6+self.IPV6_MASK } + confdb.create_entry(self.CONFIG_MUX_CABLE, "Ethernet0", fvs) + + fvs = { "server_ipv4":self.SERV2_IPV4+self.IPV4_MASK, "server_ipv6":self.SERV2_IPV6+self.IPV6_MASK } + confdb.create_entry(self.CONFIG_MUX_CABLE, "Ethernet4", fvs) + + + def set_mux_state(self, appdb, ifname, state_change): + + ps = swsscommon.ProducerStateTable(appdb, self.APP_MUX_CABLE) + + fvs = create_fvs(state=state_change) + + ps.set(ifname, fvs) + + time.sleep(1) + + + def check_neigh_in_asic_db(self, asicdb, ip, expected=1): + + nbr = asicdb.wait_for_n_keys(self.ASIC_NEIGH_TABLE, expected) + + found = False + for key in nbr: + entry = json.loads(key) + if entry["ip"] == ip: + found = True + entry = key + break + + assert found + return entry + + + def check_tnl_nexthop_in_asic_db(self, asicdb, expected=1): + + global tunnel_nh_id + + nh = asicdb.wait_for_n_keys(self.ASIC_NEXTHOP_TABLE, expected) + + for key in nh: + fvs = asicdb.get_entry(self.ASIC_NEXTHOP_TABLE, key) + if fvs.get("SAI_NEXT_HOP_ATTR_TYPE") == "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP": + tunnel_nh_id = key + + assert tunnel_nh_id + + + def check_nexthop_in_asic_db(self, asicdb, key, standby=False): + + fvs = asicdb.get_entry(self.ASIC_ROUTE_TABLE, key) + if not fvs: + assert False + + nhid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID") + if standby: + assert (nhid == tunnel_nh_id) + else: + assert (nhid != tunnel_nh_id) + + + def check_nexthop_group_in_asic_db(self, asicdb, key, num_tnl_nh=0): + + fvs = asicdb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY", key) + + nhg_id = fvs["SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID"] + + asicdb.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP", nhg_id) + + # Two NH group members are expected to be added + keys = asicdb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", 2) + + count = 0 + + for k in keys: + fvs = asicdb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER", k) + assert fvs["SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID"] == nhg_id + + # Count the number of Nexthop member pointing to tunnel + if fvs["SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID"] == tunnel_nh_id: + count += 1 + + assert num_tnl_nh == count + + + def add_neighbor(self, dvs, ip, mac, v6=False): + + if v6: + dvs.runcmd("ip -6 neigh replace " + ip + " lladdr " + mac + " dev Vlan1000") + else: + dvs.runcmd("ip -4 neigh replace " + ip + " lladdr " + mac + " dev Vlan1000") + + + def add_fdb(self, dvs, port, mac): + + appdb = dvs.get_app_db() + ps = swsscommon.ProducerStateTable(appdb.db_connection, "FDB_TABLE") + fvs = swsscommon.FieldValuePairs([("port", port), ("type", "dynamic")]) + + ps.set("Vlan1000:"+mac, fvs) + + time.sleep(1) + + + def create_and_test_neighbor(self, confdb, appdb, asicdb, dvs, dvs_route): + + self.create_vlan_interface(confdb, asicdb, dvs) + + self.create_mux_cable(confdb) + + self.set_mux_state(appdb, "Ethernet0", "active") + self.set_mux_state(appdb, "Ethernet4", "standby") + + self.add_neighbor(dvs, self.SERV1_IPV4, "00:00:00:00:00:01") + # Broadcast neigh 192.168.0.255 is default added. Hence +1 for expected number + srv1_v4 = self.check_neigh_in_asic_db(asicdb, self.SERV1_IPV4, 2) + + self.add_neighbor(dvs, self.SERV1_IPV6, "00:00:00:00:00:01", True) + srv1_v6 = self.check_neigh_in_asic_db(asicdb, self.SERV1_IPV6, 3) + + existing_keys = asicdb.get_keys(self.ASIC_NEIGH_TABLE) + + self.add_neighbor(dvs, self.SERV2_IPV4, "00:00:00:00:00:02") + self.add_neighbor(dvs, self.SERV2_IPV6, "00:00:00:00:00:02", True) + time.sleep(1) + + # In standby mode, the entry must not be added to Neigh table but Route + asicdb.wait_for_matching_keys(self.ASIC_NEIGH_TABLE, existing_keys) + dvs_route.check_asicdb_route_entries([self.SERV2_IPV4+self.IPV4_MASK, self.SERV2_IPV6+self.IPV6_MASK]) + + # The first standby route also creates as tunnel Nexthop + self.check_tnl_nexthop_in_asic_db(asicdb, 3) + + # Change state to Standby. This will delete Neigh and add Route + self.set_mux_state(appdb, "Ethernet0", "standby") + + asicdb.wait_for_deleted_entry(self.ASIC_NEIGH_TABLE, srv1_v4) + asicdb.wait_for_deleted_entry(self.ASIC_NEIGH_TABLE, srv1_v6) + dvs_route.check_asicdb_route_entries([self.SERV1_IPV4+self.IPV4_MASK, self.SERV1_IPV6+self.IPV6_MASK]) + + # Change state to Active. This will add Neigh and delete Route + self.set_mux_state(appdb, "Ethernet4", "active") + + dvs_route.check_asicdb_deleted_route_entries([self.SERV2_IPV4+self.IPV4_MASK, self.SERV2_IPV6+self.IPV6_MASK]) + self.check_neigh_in_asic_db(asicdb, self.SERV2_IPV4, 3) + self.check_neigh_in_asic_db(asicdb, self.SERV2_IPV6, 3) + + + def create_and_test_fdb(self, appdb, asicdb, dvs, dvs_route): + + self.set_mux_state(appdb, "Ethernet0", "active") + self.set_mux_state(appdb, "Ethernet4", "standby") + + self.add_fdb(dvs, "Ethernet0", "00-00-00-00-00-11") + self.add_fdb(dvs, "Ethernet4", "00-00-00-00-00-12") + + ip_1 = "fc02:1000::10" + ip_2 = "fc02:1000::11" + + self.add_neighbor(dvs, ip_1, "00:00:00:00:00:11", True) + self.add_neighbor(dvs, ip_2, "00:00:00:00:00:12", True) + + # ip_1 is on Active Mux, hence added to Host table + self.check_neigh_in_asic_db(asicdb, ip_1, 4) + + # ip_2 is on Standby Mux, hence added to Route table + dvs_route.check_asicdb_route_entries([ip_2+self.IPV6_MASK]) + + # Check ip_1 move to standby mux, should be pointing to tunnel + self.add_neighbor(dvs, ip_1, "00:00:00:00:00:12", True) + + # ip_1 moved to standby Mux, hence added to Route table + dvs_route.check_asicdb_route_entries([ip_1+self.IPV6_MASK]) + + # Check ip_2 move to active mux, should be host entry + self.add_neighbor(dvs, ip_2, "00:00:00:00:00:11", True) + + # ip_2 moved to active Mux, hence remove from Route table + dvs_route.check_asicdb_deleted_route_entries([ip_2+self.IPV6_MASK]) + self.check_neigh_in_asic_db(asicdb, ip_2, 4) + + + def create_and_test_route(self, appdb, asicdb, dvs, dvs_route): + + self.set_mux_state(appdb, "Ethernet0", "active") + + rtprefix = "2.3.4.0/24" + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route " + rtprefix + " " + self.SERV1_IPV4 + "\"") + + pdb = dvs.get_app_db() + pdb.wait_for_entry("ROUTE_TABLE", rtprefix) + + rtkeys = dvs_route.check_asicdb_route_entries([rtprefix]) + + self.check_nexthop_in_asic_db(asicdb, rtkeys[0]) + + # Change Mux state to Standby and verify route pointing to Tunnel + self.set_mux_state(appdb, "Ethernet0", "standby") + + self.check_nexthop_in_asic_db(asicdb, rtkeys[0], True) + + # Change Mux state back to Active and verify route is not pointing to Tunnel + self.set_mux_state(appdb, "Ethernet0", "active") + + self.check_nexthop_in_asic_db(asicdb, rtkeys[0]) + + # Test ECMP routes + + self.set_mux_state(appdb, "Ethernet0", "active") + self.set_mux_state(appdb, "Ethernet4", "active") + + rtprefix = "5.6.7.0/24" + + dvs_route.check_asicdb_deleted_route_entries([rtprefix]) + + ps = swsscommon.ProducerStateTable(pdb.db_connection, "ROUTE_TABLE") + + fvs = swsscommon.FieldValuePairs([("nexthop", self.SERV1_IPV4 + "," + self.SERV2_IPV4), ("ifname", "Vlan1000,Vlan1000")]) + + ps.set(rtprefix, fvs) + + # Check if route was propagated to ASIC DB + rtkeys = dvs_route.check_asicdb_route_entries([rtprefix]) + + # Check for nexthop group and validate nexthop group member in asic db + self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0]) + + # Step: 1 - Change one NH to standby and verify ecmp route + self.set_mux_state(appdb, "Ethernet0", "standby") + self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0], 1) + + # Step: 2 - Change the other NH to standby and verify ecmp route + self.set_mux_state(appdb, "Ethernet4", "standby") + self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0], 2) + + # Step: 3 - Change one NH to back to Active and verify ecmp route + self.set_mux_state(appdb, "Ethernet0", "active") + self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0], 1) + + # Step: 4 - Change the other NH to Active and verify ecmp route + self.set_mux_state(appdb, "Ethernet4", "active") + self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0]) + + def check_interface_exists_in_asicdb(self, asicdb, sai_oid): - if_table = swsscommon.Table(asicdb, self.ASIC_RIF_TABLE) - status, fvs = if_table.get(sai_oid) - return status + asicdb.wait_for_entry(self.ASIC_RIF_TABLE, sai_oid) + return True + def check_vr_exists_in_asicdb(self, asicdb, sai_oid): - vfr_table = swsscommon.Table(asicdb, self.ASIC_VRF_TABLE) - status, fvs = vfr_table.get(sai_oid) - return status + asicdb.wait_for_entry(self.ASIC_VRF_TABLE, sai_oid) + return True + def create_and_test_peer(self, db, asicdb, peer_name, peer_ip, src_ip): """ Create PEER entry verify all needed enties in ASIC DB exists """ - create_entry_tbl( - db, - "PEER_SWITCH", '|', "%s" % (peer_name), - [ - ("address_ipv4", peer_ip), - ] - ) + peer_attrs = { + "address_ipv4": peer_ip + } - time.sleep(2) + db.create_entry("PEER_SWITCH", peer_name, peer_attrs) # check asic db table - tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) - - tunnels = tunnel_table.getKeys() - # There will be two tunnels, one P2MP and another P2P - assert len(tunnels) == 2 + tunnels = asicdb.wait_for_n_keys(self.ASIC_TUNNEL_TABLE, 2) p2p_obj = None for tunnel_sai_obj in tunnels: - status, fvs = tunnel_table.get(tunnel_sai_obj) + fvs = asicdb.wait_for_entry(self.ASIC_TUNNEL_TABLE, tunnel_sai_obj) - assert status == True - - for field, value in fvs: + for field, value in fvs.items(): if field == "SAI_TUNNEL_ATTR_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" if field == "SAI_TUNNEL_ATTR_PEER_MODE": @@ -87,11 +347,9 @@ def create_and_test_peer(self, db, asicdb, peer_name, peer_ip, src_ip): assert p2p_obj != None - status, fvs = tunnel_table.get(p2p_obj) - - assert status == True + fvs = asicdb.wait_for_entry(self.ASIC_TUNNEL_TABLE, p2p_obj) - for field, value in fvs: + for field, value in fvs.items(): if field == "SAI_TUNNEL_ATTR_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" elif field == "SAI_TUNNEL_ATTR_ENCAP_SRC_IP": @@ -111,18 +369,14 @@ def create_and_test_peer(self, db, asicdb, peer_name, peer_ip, src_ip): def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips): - tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) - - tunnel_term_entries = tunnel_term_table.getKeys() - assert len(tunnel_term_entries) == len(dst_ips) + tunnel_term_entries = asicdb.wait_for_n_keys(self.ASIC_TUNNEL_TERM_ENTRIES, len(dst_ips)) for term_entry in tunnel_term_entries: - status, fvs = tunnel_term_table.get(term_entry) + fvs = asicdb.get_entry(self.ASIC_TUNNEL_TERM_ENTRIES, term_entry) - assert status == True assert len(fvs) == 5 - for field, value in fvs: + for field, value in fvs.items(): if field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID": assert self.check_vr_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE": @@ -136,6 +390,7 @@ def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid else: assert False, "Field %s is not tested" % field + def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): """ Create tunnel and verify all needed enties in ASIC DB exists """ @@ -152,16 +407,12 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): time.sleep(1) # check asic db table - tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) - - tunnels = tunnel_table.getKeys() - assert len(tunnels) == 1 + tunnels = asicdb.wait_for_n_keys(self.ASIC_TUNNEL_TABLE, 1) tunnel_sai_obj = tunnels[0] - status, fvs = tunnel_table.get(tunnel_sai_obj) + fvs = asicdb.wait_for_entry(self.ASIC_TUNNEL_TABLE, tunnel_sai_obj) - assert status == True # 6 parameters to check in case of decap tunnel # + 1 (SAI_TUNNEL_ATTR_ENCAP_SRC_IP) in case of symmetric tunnel assert len(fvs) == 7 if is_symmetric_tunnel else 6 @@ -170,7 +421,7 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): expected_dscp_mode = self.dscp_modes_map[kwargs["dscp_mode"]] expected_ttl_mode = self.ttl_modes_map[kwargs["ttl_mode"]] - for field, value in fvs: + for field, value in fvs.items(): if field == "SAI_TUNNEL_ATTR_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" elif field == "SAI_TUNNEL_ATTR_ENCAP_SRC_IP": @@ -190,6 +441,7 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): self.check_tunnel_termination_entry_exists_in_asicdb(asicdb, tunnel_sai_obj, kwargs["dst_ip"].split(",")) + def remove_and_test_tunnel(self, db, asicdb, tunnel_name): """ Removes tunnel and checks that ASIC db is clear""" @@ -216,18 +468,19 @@ def remove_and_test_tunnel(self, db, asicdb, tunnel_name): assert len(tunnel_app_table.getKeys()) == 0 assert not self.check_interface_exists_in_asicdb(asicdb, overlay_infs_id) + def cleanup_left_over(self, db, asicdb): """ Cleanup APP and ASIC tables """ - tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) - for key in tunnel_table.getKeys(): - tunnel_table._del(key) + tunnel_table = asicdb.get_keys(self.ASIC_TUNNEL_TABLE) + for key in tunnel_table: + asicdb.delete_entry(self.ASIC_TUNNEL_TABLE, key) - tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) - for key in tunnel_term_table.getKeys(): - tunnel_term_table._del(key) + tunnel_term_table = asicdb.get_keys(self.ASIC_TUNNEL_TERM_ENTRIES) + for key in tunnel_term_table: + asicdb.delete_entry(self.ASIC_TUNNEL_TERM_ENTRIES, key) - tunnel_app_table = swsscommon.Table(asicdb, self.APP_TUNNEL_DECAP_TABLE_NAME) + tunnel_app_table = swsscommon.Table(db, self.APP_TUNNEL_DECAP_TABLE_NAME) for key in tunnel_app_table.getKeys(): tunnel_table._del(key) @@ -239,9 +492,9 @@ def test_Tunnel(self, dvs, testlog): """ test IPv4 Mux tunnel creation """ db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + asicdb = dvs.get_asic_db() - self.cleanup_left_over(db, asicdb) + #self.cleanup_left_over(db, asicdb) # create tunnel IPv4 tunnel self.create_and_test_tunnel(db, asicdb, tunnel_name="MuxTunnel0", tunnel_type="IPINIP", @@ -252,12 +505,40 @@ def test_Tunnel(self, dvs, testlog): def test_Peer(self, dvs, testlog): """ test IPv4 Mux tunnel creation """ - db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + db = dvs.get_config_db() + asicdb = dvs.get_asic_db() self.create_and_test_peer(db, asicdb, "peer", "1.1.1.1", "10.1.0.32") + def test_Neighbor(self, dvs, dvs_route, testlog): + """ test Neighbor entries and mux state change """ + + confdb = dvs.get_config_db() + appdb = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = dvs.get_asic_db() + + self.create_and_test_neighbor(confdb, appdb, asicdb, dvs, dvs_route) + + + def test_Fdb(self, dvs, dvs_route, testlog): + """ test Fdb entries and mux state change """ + + appdb = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = dvs.get_asic_db() + + self.create_and_test_fdb(appdb, asicdb, dvs, dvs_route) + + + def test_Route(self, dvs, dvs_route, testlog): + """ test Route entries and mux state change """ + + appdb = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = dvs.get_asic_db() + + self.create_and_test_route(appdb, asicdb, dvs, dvs_route) + + # 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(): From 90318679f5f831f415de65e07534803b49076afd Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:30:26 -0800 Subject: [PATCH 34/54] Keep attribute order in bulk mode (#1659) Keep attributes for the same entry in the order that the attributes are set in bulk mode. And add a unit test for the scenario in bulker. Some attributes have a dependency on others in SAI operations (e.g., setting nexthop requires packet action to be forward in route set operation). Without keeping the order of the attributes, the configuration may not be successfully applied to hardware. Therefore, it is necessary to keep the order of attributes in bulk mode. --- orchagent/bulker.h | 45 +++++--------- tests/mock_tests/Makefile.am | 1 + tests/mock_tests/bulker_ut.cpp | 106 +++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 29 deletions(-) create mode 100644 tests/mock_tests/bulker_ut.cpp diff --git a/orchagent/bulker.h b/orchagent/bulker.h index e3e464c62d..4f6e9cc5cb 100644 --- a/orchagent/bulker.h +++ b/orchagent/bulker.h @@ -208,10 +208,10 @@ class EntityBulker if (found_setting != setting_entries.end()) { // Mark old one as done - auto& attrmap = found_setting->second; - for (auto& attr: attrmap) + auto& attrs = found_setting->second; + for (auto& attr: attrs) { - *attr.second.second = SAI_STATUS_SUCCESS; + *attr.second = SAI_STATUS_SUCCESS; } // Erase old one setting_entries.erase(found_setting); @@ -252,27 +252,15 @@ class EntityBulker if (!attr) throw std::invalid_argument("attr is null"); // Insert or find the key (entry) - auto& attrmap = setting_entries.emplace(std::piecewise_construct, + auto& attrs = setting_entries.emplace(std::piecewise_construct, std::forward_as_tuple(*entry), std::forward_as_tuple() ).first->second; - // Insert or find the key (attr) - auto rc = attrmap.emplace(std::piecewise_construct, - std::forward_as_tuple(attr->id), - std::forward_as_tuple()); - bool inserted = rc.second; - auto it = rc.first; - - // If inserted new key, assign the attr - // If found existing key, overwrite the old attr - it->second.first = *attr; - if (!inserted) - { - // If found existing key, mark old status as success - *it->second.second = SAI_STATUS_SUCCESS; - } - it->second.second = object_status; + // Insert attr + attrs.emplace_back(std::piecewise_construct, + std::forward_as_tuple(*attr), + std::forward_as_tuple(object_status)); *object_status = SAI_STATUS_NOT_EXECUTED; } @@ -351,19 +339,21 @@ class EntityBulker { std::vector rs; std::vector ts; + std::vector status_vector; for (auto const& i: setting_entries) { auto const& entry = i.first; - auto const& attrmap = i.second; - for (auto const& ia: attrmap) + auto const& attrs = i.second; + for (auto const& ia: attrs) { - auto const& attr = ia.second.first; - sai_status_t *object_status = ia.second.second; + auto const& attr = ia.first; + sai_status_t *object_status = ia.second; if (*object_status == SAI_STATUS_NOT_EXECUTED) { rs.push_back(entry); ts.push_back(attr); + status_vector.push_back(object_status); } } } @@ -375,9 +365,7 @@ class EntityBulker for (size_t ir = 0; ir < count; ir++) { - auto& entry = rs[ir]; - auto& attr_id = ts[ir].id; - sai_status_t *object_status = setting_entries[entry][attr_id].second; + sai_status_t *object_status = status_vector[ir]; if (object_status) { SWSS_LOG_INFO("EntityBulker.flush setting_entries status[%zu]=%d(0x%8p)\n", ir, statuses[ir], object_status); @@ -426,8 +414,7 @@ class EntityBulker std::unordered_map< // A map of Te, // entry -> - std::unordered_map< // another map of - sai_attr_id_t, // attr_id -> + std::vector< // vector of attribute and status std::pair< sai_attribute_t, // (attr_value, OUT object_status) sai_status_t * diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 050cd5274c..80f9e7173e 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -31,6 +31,7 @@ tests_SOURCES = aclorch_ut.cpp \ mock_table.cpp \ mock_hiredis.cpp \ mock_redisreply.cpp \ + bulker_ut.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ $(top_srcdir)/orchagent/orchdaemon.cpp \ $(top_srcdir)/orchagent/orch.cpp \ diff --git a/tests/mock_tests/bulker_ut.cpp b/tests/mock_tests/bulker_ut.cpp new file mode 100644 index 0000000000..1cdabcdd14 --- /dev/null +++ b/tests/mock_tests/bulker_ut.cpp @@ -0,0 +1,106 @@ +#include "ut_helper.h" +#include "bulker.h" + +extern sai_route_api_t *sai_route_api; + +namespace bulker_test +{ + using namespace std; + + struct BulkerTest : public ::testing::Test + { + BulkerTest() + { + } + + void SetUp() override + { + ASSERT_EQ(sai_route_api, nullptr); + sai_route_api = new sai_route_api_t(); + } + + void TearDown() override + { + delete sai_route_api; + sai_route_api = nullptr; + } + }; + + TEST_F(BulkerTest, BulkerAttrOrder) + { + // Create bulker + EntityBulker gRouteBulker(sai_route_api); + deque object_statuses; + + // Create a dummy route entry + sai_route_entry_t route_entry; + route_entry.destination.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + route_entry.destination.addr.ip4 = htonl(0x0a00000f); + route_entry.destination.mask.ip4 = htonl(0xffffff00); + route_entry.vr_id = 0x0; + route_entry.switch_id = 0x0; + + // Set packet action for route first + sai_attribute_t route_attr; + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + + // Set next hop for route + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + route_attr.value.oid = SAI_NULL_OBJECT_ID; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + + // Check number of routes in bulk + ASSERT_EQ(gRouteBulker.setting_entries_count(), 1); + + // Confirm the order of attributes in bulk is the same as being set + auto const& attrs = gRouteBulker.setting_entries[route_entry]; + ASSERT_EQ(attrs.size(), 2); + auto ia = attrs.begin(); + ASSERT_EQ(ia->first.id, SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION); + ASSERT_EQ(ia->first.value.s32, SAI_PACKET_ACTION_FORWARD); + ia++; + ASSERT_EQ(ia->first.id, SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID); + ASSERT_EQ(ia->first.value.oid, SAI_NULL_OBJECT_ID); + + // Clear the bulk + gRouteBulker.clear(); + object_statuses.clear(); + + // Check the bulker has been cleared + ASSERT_EQ(gRouteBulker.setting_entries_count(), 0); + + // Test the inverse order + // Set next hop for route first + route_attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID; + route_attr.value.oid = SAI_NULL_OBJECT_ID; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + + // Set packet action for route + route_attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION; + route_attr.value.s32 = SAI_PACKET_ACTION_FORWARD; + + object_statuses.emplace_back(); + gRouteBulker.set_entry_attribute(&object_statuses.back(), &route_entry, &route_attr); + + // Check number of routes in bulk + ASSERT_EQ(gRouteBulker.setting_entries_count(), 1); + + // Confirm the order of attributes in bulk is the same as being set + auto const& attrs_reverse = gRouteBulker.setting_entries[route_entry]; + ASSERT_EQ(attrs_reverse.size(), 2); + ia = attrs_reverse.begin(); + ASSERT_EQ(ia->first.id, SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID); + ASSERT_EQ(ia->first.value.oid, SAI_NULL_OBJECT_ID); + ia++; + ASSERT_EQ(ia->first.id, SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION); + ASSERT_EQ(ia->first.value.s32, SAI_PACKET_ACTION_FORWARD); + } +} From 85ff67c4c490abfa5b178edbbb77d10af1742c88 Mon Sep 17 00:00:00 2001 From: RajeshPukhrajJain <79225301+RajeshPukhrajJain@users.noreply.github.com> Date: Mon, 8 Mar 2021 15:01:47 -0800 Subject: [PATCH 35/54] Remove vxlanmgrd dependency on orchagent (#1647) Removed the dependency of vxlanmgr to wait for the swss to reconcile during the warmboot process. --- cfgmgr/vxlanmgrd.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/cfgmgr/vxlanmgrd.cpp b/cfgmgr/vxlanmgrd.cpp index 19b827b17a..809c580f82 100644 --- a/cfgmgr/vxlanmgrd.cpp +++ b/cfgmgr/vxlanmgrd.cpp @@ -94,27 +94,9 @@ int main(int argc, char **argv) if (WarmStart::isWarmStart()) { - WarmStart::WarmStartState state; - vxlanmgr.waitTillReadyToReconcile(); vxlanmgr.restoreVxlanNetDevices(); WarmStart::setWarmStartState("vxlanmgrd", WarmStart::REPLAYED); - uint16_t wait_secs = 0; - string val = ""; - Table wb_tbl = Table(&stateDb, STATE_WARM_RESTART_TABLE_NAME); - wb_tbl.hget("orchagent", "restore_count", val); - if ((val != "") or (val != "0")) - { - WarmStart::getWarmStartState("orchagent",state); - while (state != WarmStart::RECONCILED) - { - SWSS_LOG_NOTICE("Waiting Until Orchagent is reconciled. Current %s. Waited %u secs", - val.c_str(), wait_secs); - sleep(1); - wait_secs++; - WarmStart::getWarmStartState("orchagent",state); - } - } } SWSS_LOG_NOTICE("starting main loop"); From 9e30abb045fd0f20b4bb636c22d04fb89cd7b5c2 Mon Sep 17 00:00:00 2001 From: Wenda Ni Date: Tue, 9 Mar 2021 16:15:57 -0800 Subject: [PATCH 36/54] [vstest/subintf] Add vs test case to validate processing sequence of APPL DB keys (#1663) Signed-off-by: Wenda Ni --- tests/test_sub_port_intf.py | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py index 6a135e6cd1..5074599545 100644 --- a/tests/test_sub_port_intf.py +++ b/tests/test_sub_port_intf.py @@ -34,6 +34,7 @@ LAG_PREFIX = "PortChannel" VLAN_SUB_INTERFACE_SEPARATOR = "." +APPL_DB_SEPARATOR = ":" class TestSubPortIntf(object): @@ -102,12 +103,32 @@ def add_lag_members(self, lag, members): key = "{}|{}".format(lag, member) self.config_db.create_entry(CFG_LAG_MEMBER_TABLE_NAME, key, fvs) + def create_sub_port_intf_profile_appl_db(self, sub_port_intf_name, admin_status): + pairs = [ + (ADMIN_STATUS, admin_status), + ("mtu", "0"), + ] + fvs = swsscommon.FieldValuePairs(pairs) + + tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_INTF_TABLE_NAME) + tbl.set(sub_port_intf_name, fvs) + def add_sub_port_intf_ip_addr(self, sub_port_intf_name, ip_addr): fvs = {"NULL": "NULL"} key = "{}|{}".format(sub_port_intf_name, ip_addr) self.config_db.create_entry(CFG_VLAN_SUB_INTF_TABLE_NAME, key, fvs) + def add_sub_port_intf_ip_addr_appl_db(self, sub_port_intf_name, ip_addr): + pairs = [ + ("scope", "global"), + ("family", "IPv4" if "." in ip_addr else "IPv6"), + ] + fvs = swsscommon.FieldValuePairs(pairs) + + tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_INTF_TABLE_NAME) + tbl.set(sub_port_intf_name + APPL_DB_SEPARATOR + ip_addr, fvs) + def set_sub_port_intf_admin_status(self, sub_port_intf_name, status): fvs = {ADMIN_STATUS: status} @@ -124,6 +145,10 @@ def remove_sub_port_intf_profile(self, sub_port_intf_name): def check_sub_port_intf_profile_removal(self, rif_oid): self.asic_db.wait_for_deleted_keys(ASIC_RIF_TABLE, [rif_oid]) + def remove_sub_port_intf_profile_appl_db(self, sub_port_intf_name): + tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_INTF_TABLE_NAME) + tbl._del(sub_port_intf_name) + def remove_sub_port_intf_ip_addr(self, sub_port_intf_name, ip_addr): key = "{}|{}".format(sub_port_intf_name, ip_addr) self.config_db.delete_entry(CFG_VLAN_SUB_INTF_TABLE_NAME, key) @@ -132,6 +157,10 @@ def check_sub_port_intf_ip_addr_removal(self, sub_port_intf_name, ip_addrs): interfaces = ["{}:{}".format(sub_port_intf_name, addr) for addr in ip_addrs] self.app_db.wait_for_deleted_keys(APP_INTF_TABLE_NAME, interfaces) + def remove_sub_port_intf_ip_addr_appl_db(self, sub_port_intf_name, ip_addr): + tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_INTF_TABLE_NAME) + tbl._del(sub_port_intf_name + APPL_DB_SEPARATOR + ip_addr) + def get_oids(self, table): return self.asic_db.get_keys(table) @@ -320,6 +349,50 @@ def test_sub_port_intf_add_ip_addrs(self, dvs): self._test_sub_port_intf_add_ip_addrs(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST) self._test_sub_port_intf_add_ip_addrs(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST) + def _test_sub_port_intf_appl_db_proc_seq(self, dvs, sub_port_intf_name, admin_up): + substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = substrs[0] + vlan_id = substrs[1] + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(dvs, parent_port, "up") + + # Create ip address configuration in APPL_DB before creating configuration for sub port interface itself + self.add_sub_port_intf_ip_addr_appl_db(sub_port_intf_name, self.IPV4_ADDR_UNDER_TEST) + self.add_sub_port_intf_ip_addr_appl_db(sub_port_intf_name, self.IPV6_ADDR_UNDER_TEST) + time.sleep(2) + + # Create sub port interface configuration in APPL_DB + self.create_sub_port_intf_profile_appl_db(sub_port_intf_name, "up" if admin_up == True else "down") + + # Verify that a sub port router interface entry is created in ASIC_DB + fv_dict = { + "SAI_ROUTER_INTERFACE_ATTR_TYPE": "SAI_ROUTER_INTERFACE_TYPE_SUB_PORT", + "SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID": "{}".format(vlan_id), + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE": "true" if admin_up == True else "false", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE": "true" if admin_up == True else "false", + "SAI_ROUTER_INTERFACE_ATTR_MTU": DEFAULT_MTU, + } + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + + # Remove ip addresses from APPL_DB + self.remove_sub_port_intf_ip_addr_appl_db(sub_port_intf_name, self.IPV4_ADDR_UNDER_TEST) + self.remove_sub_port_intf_ip_addr_appl_db(sub_port_intf_name, self.IPV6_ADDR_UNDER_TEST) + # Remove sub port interface from APPL_DB + self.remove_sub_port_intf_profile_appl_db(sub_port_intf_name) + self.check_sub_port_intf_profile_removal(rif_oid) + + def test_sub_port_intf_appl_db_proc_seq(self, dvs): + self.connect_dbs(dvs) + + self._test_sub_port_intf_appl_db_proc_seq(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST, admin_up=True) + self._test_sub_port_intf_appl_db_proc_seq(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST, admin_up=False) + + self._test_sub_port_intf_appl_db_proc_seq(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST, admin_up=True) + self._test_sub_port_intf_appl_db_proc_seq(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST, admin_up=False) + def _test_sub_port_intf_admin_status_change(self, dvs, sub_port_intf_name): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] From b0c2a74b82af3ed9fa0d408d0ff665e05540e621 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Thu, 11 Mar 2021 07:13:04 +0800 Subject: [PATCH 37/54] Add table descriptions for dynamic buffer calculation to the documents (#1664) **What I did** Add table descriptions for dynamic buffer calculation to the documents. **Why I did it** To make the documents aligned with the code. --- doc/Configuration.md | 100 ++++++++++++++++++++++++++++++++++++++++++- doc/swss-schema.md | 61 ++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/doc/Configuration.md b/doc/Configuration.md index 7a1941fc31..1daac7b328 100644 --- a/doc/Configuration.md +++ b/doc/Configuration.md @@ -17,13 +17,15 @@ Table of Contents * [Cable length](#cable-length) * [COPP_TABLE](#copp_table) * [CRM](#crm) - * [Data Plane L3 Interfaces](#data-plane-l3-interfaces) + * [Data Plane L3 Interfaces](#data-plane-l3-interfaces) + * [DEFAULT_LOSSLESS_BUFFER_PARAMETER](#DEFAULT_LOSSLESS_BUFFER_PARAMETER) * [Device Metadata](#device-metadata) * [Device neighbor metada](#device-neighbor-metada) * [DSCP_TO_TC_MAP](#dscp_to_tc_map) * [FLEX_COUNTER_TABLE](#flex_counter_table) * [L2 Neighbors](#l2-neighbors) * [Loopback Interface](#loopback-interface) + * [LOSSLESS_TRAFFIC_PATTERN](#LOSSLESS_TRAFFIC_PATTERN) * [Management Interface](#management-interface) * [Management port](#management-port) * [Management VRF](#management-vrf) @@ -334,6 +336,8 @@ group name and IP ranges in **BGP_PEER_RANGE** table. ### BUFFER_PG +When the system is running in traditional buffer model, profiles needs to explicitly configured: + ``` { "BUFFER_PG": { @@ -351,8 +355,32 @@ group name and IP ranges in **BGP_PEER_RANGE** table. ``` +When the system is running in dynamic buffer model, profiles can be: + + - either calculated dynamically according to ports' configuration and just configured as "NULL"; + - or configured explicitly. + +``` +{ +"BUFFER_PG": { + "Ethernet0|3-4": { + "profile": "NULL" + }, + "Ethernet1|3-4": { + "profile": "NULL" + }, + "Ethernet2|3-4": { + "profile": "[BUFFER_PROFILE|static_profile]" + } + } +} + +``` + ### Buffer pool +When the system is running in traditional buffer model, the size of all of the buffer pools and xoff of ingress_lossless_pool need to be configured explicitly. + ``` { "BUFFER_POOL": { @@ -377,6 +405,29 @@ group name and IP ranges in **BGP_PEER_RANGE** table. ``` +When the system is running in dynamic buffer model, the size of some of the buffer pools can be omitted and will be dynamically calculated. + +``` +{ +"BUFFER_POOL": { + "egress_lossless_pool": { + "type": "egress", + "mode": "static", + "size": "15982720" + }, + "egress_lossy_pool": { + "type": "egress", + "mode": "dynamic", + }, + "ingress_lossless_pool": { + "type": "ingress", + "mode": "dynamic", + } + } +} + +``` + ### Buffer profile @@ -419,6 +470,19 @@ group name and IP ranges in **BGP_PEER_RANGE** table. ``` +When the system is running in dynamic buffer model and the headroom_type is dynamic, only dynamic_th needs to be configured and rest of fields can be omitted. +This kind of profiles will be handled by buffer manager and won't be applied to SAI. + +``` +{ + { + "non_default_dynamic_th_profile": { + "dynamic_th": 1, + "headroom_type": "dynamic" + } + } +} +``` ### Buffer queue @@ -588,6 +652,21 @@ attributes. ``` +### DEFAULT_LOSSLESS_BUFFER_PARAMETER + +This table stores the default lossless buffer parameters for dynamic buffer calculation. + +``` +{ + "DEFAULT_LOSSLESS_BUFFER_PARAMETER": { + "AZURE": { + "default_dynamic_th": "0", + "over_subscribe_ratio": "2" + } + } +} +``` + ### Device Metadata The **DEVICE_METADATA** table contains only one object named @@ -609,7 +688,8 @@ instance is supported in SONiC. "default_pfcwd_status": "disable", "bgp_asn": "65100", "deployment_id": "1", - "type": "ToRRouter" + "type": "ToRRouter", + "buffer_model": "traditional" } } } @@ -746,6 +826,22 @@ interface objects. ``` +### LOSSLESS_TRAFFIC_PATTERN + +The LOSSLESS_TRAFFIC_PATTERN table stores parameters related to +lossless traffic for dynamic buffer calculation + +``` +{ + "LOSSLESS_TRAFFIC_PATTERN": { + "AZURE": { + "mtu": "1024", + "small_packet_percentage": "100" + } + } +} +``` + ### Management Interface Management interfaces are defined in **MGMT_INTERFACE** table. Object diff --git a/doc/swss-schema.md b/doc/swss-schema.md index af3b063146..7f25803a28 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -803,6 +803,59 @@ packet_action = "drop" | "forward" | "copy" | "copy_cancel" | "trap" | "log" | " nexthop = IP ; Nexthop IP address (Optional) ifname = ifname ; Interface name +### BUFFER_POOL_TABLE + ;Stores buffer pools + + key = BUFFER_POOL_TABLE:poolname ; The poolname can be one of ingress_lossless_pool, ingress_lossy_pool, egress_lossless_pool, and egress_lossy_pool or other used-defined pools. + mode = "dynamic" / "static" ; Whether the pool uses dynamic threshold or static threshold. + type = "ingress" / "egress" ; Whether the pool serves for ingress or egress traffic + size = 1*10DIGIT ; The size of the shared buffer pool + xoff = 1*10DIGIT ; The size of the shared headroom pool. Available only for ingress_lossless_pool. + +### BUFFER_PROFILE_TABLE + ;Stores buffer profiles + + key = BUFFER_PROFILE_TABLE:profilename ; profile name can be predefined or dynamically generated with name convention "pg_lossless___profile" + pool = reference to BUFFER_POOL_TABLE object + xon = 1*6DIGIT ; The xon threshold. The switch stops sending PFC frame when the buffer occupancy drops to this threshold. + xon_offset = 1*6DIGIT ; The xon offset. If both xon and xon_offset have been defined, the switch stops sending PFC frame + ; when the buffer occupancy drops to xon or size of buffer pool size minus xon_offset, whichever is larger. + xoff = 1*6DIGIT ; The xoff threshold. The switch starts sending PFC frame when the buffer occupancy rises to this threshold. + size = 1*6DIGIT ; The reserved size of the PG or queue referencing this buffer profile. + dynamic_th = 1*2DIGIT ; For dynamic pools, representing the proportion of currently available memory in the pool the PG or queue can occupy. + ; It is calculated as: + ; alpha = 2 ^ dynamic_th; + ; proportion = alpha / (1 + alpha) + static_th = 1*10DIGIT ; For static pools, representing the threshold in bytes the PG or queue can occupy. + +### BUFFER_PG_TABLE + ;Stores buffer PG (priority-groups) + + key = BUFFER_PG_TABLE:port_name:pg ; The pg consists of a single number representing a single priority or two numbers connected by a dash representing a range of priorities. + ; By default, PG 0 for lossy traffic and PG 3-4 for lossless traffic. + profile = reference to BUFFER_PROFILE_TABLE object + +### BUFFER_QUEUE_TABLE + ;Stores buffer queue + + key = BUFFER_QUEUE_TABLE:port_name:queue ; The queue consists of a single number representing a single priority or two numbers connected by a dash representing a range of priorities. + ; By default, queue 0-2 and 5-6 for lossy traffic and queue 3-4 for lossless traffic + profile = reference to BUFFER_PROFILE_TABLE object + +### BUFFER_PORT_INGRESS_PROFILE_LIST_TABLE + ;Stores per port buffer threshold on ingress side + + key = BUFFER_PORT_INGRESS_PROFILE_LIST_TABLE:port_name + profile_list = a list of references to BUFFER_PROFILE_TABLE object ; Typically, for each ingress buffer pools there should be a buffer profile referencing the pool in the list. + ; For example, if there are two ingress buffer pools in the system, ingress_lossless_pool and ingress_lossy_pool, + ; there should be two profiles in the list: ingress_lossless_profile and ingress_lossy_profile + +### BUFFER_PORT_EGRESS_PROFILE_LIST_TABLE + ;Stores per port buffer threshold on egress side + + key = BUFFER_PORT_EGRESS_PROFILE_LIST_TABLE:port_name + profile_list = a list of references to BUFFER_PROFILE_TABLE object ; Similar to profile_list in BUFFER_PORT_INGRESS_PROFILE_LIST_TABLE but on egress side. + ## Configuration DB schema ### PORT_TABLE @@ -994,6 +1047,14 @@ Stores information for physical switch ports managed by the switch chip. Ports t key = VRF_OBJECT_TABLE|vrf_name ; vrf_name start with 'Vrf' prefix state = "ok" ; vrf entry exist in orchagent +### BUFFER_MAX_PARAM_TABLE + ;Available only when the switch is running in dynamic buffer model + ;Stores the maximum available buffer on a global or per-port basis + + key = BUFFER_MAX_PARAM_TABLE|ifname ; The maximum headroom of the port. The ifname should be the name of a physical port. + BUFFER_MAX_PARAM_TABLE|global ; The maximum available of the system. + mmu_size = 1*10DIGIT ; The maximum available of the system. Available only when the key is "global". + max_headroom_size = 1*10DIGIT ; The maximum headroom of the port. Available only when the key is ifname. ## Configuration files What configuration files should we have? Do apps, orch agent each need separate files? From 195136512ac1242a4f2a038240b0fe8b098b6d96 Mon Sep 17 00:00:00 2001 From: vganesan-nokia <67648637+vganesan-nokia@users.noreply.github.com> Date: Wed, 10 Mar 2021 19:39:50 -0500 Subject: [PATCH 38/54] [vog/systemlag] Voq lagid allocator (#1603) What I did Defined class for lag id allocator and added lua script for allocating/freeing lag id in atomic fashion Why I did it For portchannels in VOQ based chassis systems we need unique lag id across the system. The lag id (aka system port aggreggator id) is allocated during portchannel creation. The changes are for a class for lag id allocation in atomic fashion. The LAG ID is allocated from central chassis app db. A lua script loaded in the redis at the time of lag id allocator instantiation ensures allocating unique lag id when multiple clients requests for lag id simultaneously. Ref: VOQ LAG HLD PR: Azure/SONiC#697 --- orchagent/lagid.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++ orchagent/lagid.h | 44 ++++++++++++++++++++ orchagent/lagids.lua | 88 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 orchagent/lagid.cpp create mode 100644 orchagent/lagid.h create mode 100644 orchagent/lagids.lua diff --git a/orchagent/lagid.cpp b/orchagent/lagid.cpp new file mode 100644 index 0000000000..45f489639f --- /dev/null +++ b/orchagent/lagid.cpp @@ -0,0 +1,96 @@ +#include "lagid.h" + +LagIdAllocator::LagIdAllocator( + _In_ DBConnector* chassis_app_db) +{ + SWSS_LOG_ENTER(); + + m_dbConnector = chassis_app_db; + + // Load lua script to allocate system lag id. This lua script ensures allocation + // of unique system lag id from global chassis app db in atomic fashion when allocation + // is requested by different asic instances simultaneously + + string luaScript = loadLuaScript("lagids.lua"); + m_shaLagId = loadRedisScript(m_dbConnector, luaScript); +} + +int32_t LagIdAllocator::lagIdAdd( + _In_ const string &pcname, + _In_ int32_t lag_id) +{ + SWSS_LOG_ENTER(); + + // No keys + vector keys; + + vector args; + args.push_back("add"); + args.push_back(pcname); + args.push_back(to_string(lag_id)); + + set ret = runRedisScript(*m_dbConnector, m_shaLagId, keys, args); + + if (!ret.empty()) + { + // We expect only one value in the set returned + + auto rv_lag_id = ret.begin(); + + return (stoi(*rv_lag_id)); + } + + return LAG_ID_ALLOCATOR_ERROR_DB_ERROR; +} + +int32_t LagIdAllocator::lagIdDel( + _In_ const string &pcname) +{ + SWSS_LOG_ENTER(); + + // No keys + vector keys; + + vector args; + args.push_back("del"); + args.push_back(pcname); + + set ret = runRedisScript(*m_dbConnector, m_shaLagId, keys, args); + + if (!ret.empty()) + { + // We expect only one value in the set returned + + auto rv_lag_id = ret.begin(); + + return (stoi(*rv_lag_id)); + } + + return LAG_ID_ALLOCATOR_ERROR_DB_ERROR; +} + +int32_t LagIdAllocator::lagIdGet( + _In_ const string &pcname) +{ + SWSS_LOG_ENTER(); + + // No keys + vector keys; + + vector args; + args.push_back("get"); + args.push_back(pcname); + + set ret = runRedisScript(*m_dbConnector, m_shaLagId, keys, args); + + if (!ret.empty()) + { + // We expect only one value in the set returned + + auto rv_lag_id = ret.begin(); + + return (stoi(*rv_lag_id)); + } + + return LAG_ID_ALLOCATOR_ERROR_DB_ERROR; +} diff --git a/orchagent/lagid.h b/orchagent/lagid.h new file mode 100644 index 0000000000..9b043741a1 --- /dev/null +++ b/orchagent/lagid.h @@ -0,0 +1,44 @@ +#ifndef SWSS_LAGID_H +#define SWSS_LAGID_H + +#include "dbconnector.h" +#include "sal.h" +#include "schema.h" +#include "redisapi.h" + +using namespace swss; +using namespace std; + +#define LAG_ID_ALLOCATOR_ERROR_DELETE_ENTRY_NOT_FOUND 0 +#define LAG_ID_ALLOCATOR_ERROR_TABLE_FULL -1 +#define LAG_ID_ALLOCATOR_ERROR_GET_ENTRY_NOT_FOUND -2 +#define LAG_ID_ALLOCATOR_ERROR_INVALID_OP -3 +#define LAG_ID_ALLOCATOR_ERROR_DB_ERROR -4 + +class LagIdAllocator +{ +public: + + LagIdAllocator( + _In_ DBConnector* chassis_app_db); + +public: + + int32_t lagIdAdd( + _In_ const string &pcname, + _In_ int32_t lag_id); + + int32_t lagIdDel( + _In_ const string &pcname); + + int32_t lagIdGet( + _In_ const string &pcname); + +private: + + DBConnector* m_dbConnector; + + string m_shaLagId; +}; + +#endif // SWSS_LAGID_H diff --git a/orchagent/lagids.lua b/orchagent/lagids.lua new file mode 100644 index 0000000000..93a546cad1 --- /dev/null +++ b/orchagent/lagids.lua @@ -0,0 +1,88 @@ +-- KEYS - None +-- ARGV[1] - operation (add/del/get) +-- ARGV[2] - lag name +-- ARGV[3] - current lag id (for "add" operation only) + +-- return lagid if success for "add"/"del" +-- return 0 if lag does not exist for "del" +-- return -1 if lag table full for "add" +-- return -2 if lag does not exist for "get" +-- return -3 if invalid operation + +local op = ARGV[1] +local pcname = ARGV[2] + +local lagid_start = tonumber(redis.call("get", "SYSTEM_LAG_ID_START")) +local lagid_end = tonumber(redis.call("get", "SYSTEM_LAG_ID_END")) + +if op == "add" then + + local plagid = tonumber(ARGV[3]) + + local dblagid = redis.call("hget", "SYSTEM_LAG_ID_TABLE", pcname) + + if dblagid then + dblagid = tonumber(dblagid) + if plagid == 0 then + -- no lagid proposed. Return the existing lagid + return dblagid + end + end + + -- lagid allocation request with a lagid proposal + if plagid >= lagid_start and plagid <= lagid_end then + if plagid == dblagid then + -- proposed lagid is same as the lagid in database + return plagid + end + -- proposed lag id is different than that in database OR + -- the portchannel does not exist in the database + -- If proposed lagid is available, return the same proposed lag id + if redis.call("sismember", "SYSTEM_LAG_ID_SET", tostring(plagid)) == 0 then + redis.call("sadd", "SYSTEM_LAG_ID_SET", tostring(plagid)) + redis.call("srem", "SYSTEM_LAG_ID_SET", tostring(dblagid)) + redis.call("hset", "SYSTEM_LAG_ID_TABLE", pcname, tostring(plagid)) + return plagid + end + end + + local lagid = lagid_start + while lagid <= lagid_end do + if redis.call("sismember", "SYSTEM_LAG_ID_SET", tostring(lagid)) == 0 then + redis.call("sadd", "SYSTEM_LAG_ID_SET", tostring(lagid)) + redis.call("srem", "SYSTEM_LAG_ID_SET", tostring(dblagid)) + redis.call("hset", "SYSTEM_LAG_ID_TABLE", pcname, tostring(lagid)) + return lagid + end + lagid = lagid + 1 + end + + return -1 + +end + +if op == "del" then + + if redis.call("hexists", "SYSTEM_LAG_ID_TABLE", pcname) == 1 then + local lagid = redis.call("hget", "SYSTEM_LAG_ID_TABLE", pcname) + redis.call("srem", "SYSTEM_LAG_ID_SET", lagid) + redis.call("hdel", "SYSTEM_LAG_ID_TABLE", pcname) + return tonumber(lagid) + end + + return 0 + +end + +if op == "get" then + + if redis.call("hexists", "SYSTEM_LAG_ID_TABLE", pcname) == 1 then + local lagid = redis.call("hget", "SYSTEM_LAG_ID_TABLE", pcname) + return tonumber(lagid) + end + + return -2 + +end + +return -3 From e6d97900a9aec3b18cc757b0a0eb138e1d19c051 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Wed, 17 Mar 2021 11:13:48 -0700 Subject: [PATCH 39/54] [MUX/PFCWD] Use in_ports for acls instead of seperate ACL table (#1670) *Use ACL_TABLE_DROP for both PFC_WD and MUX *Use MATCH_IN_PORTS instead of binding port to ACL table and program single ACL rule --- orchagent/aclorch.cpp | 40 ++++++++++++++++--- orchagent/aclorch.h | 3 +- orchagent/muxorch.cpp | 72 ++++++++++++++++++++++++++++------ orchagent/muxorch.h | 5 ++- orchagent/pfcactionhandler.cpp | 32 +++++++++------ tests/test_mux.py | 49 +++++++++++++++++++++++ 6 files changed, 168 insertions(+), 33 deletions(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index c24eeedb6d..02b5d47177 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -2961,7 +2961,26 @@ bool AclOrch::removeAclRule(string table_id, string rule_id) return m_AclTables[table_oid].remove(rule_id); } -bool AclOrch::updateAclRule(shared_ptr rule, string table_id, string attr_name, void *data, bool oper) +AclRule* AclOrch::getAclRule(string table_id, string rule_id) +{ + sai_object_id_t table_oid = getTableById(table_id); + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Table %s does not exist", table_id.c_str()); + return nullptr; + } + + const auto& rule_it = m_AclTables[table_oid].rules.find(rule_id); + if (rule_it == m_AclTables[table_oid].rules.end()) + { + SWSS_LOG_INFO("Rule %s doesn't exist", rule_id.c_str()); + return nullptr; + } + + return rule_it->second.get(); +} + +bool AclOrch::updateAclRule(string table_id, string rule_id, string attr_name, void *data, bool oper) { SWSS_LOG_ENTER(); @@ -2974,12 +2993,19 @@ bool AclOrch::updateAclRule(shared_ptr rule, string table_id, string at return false; } + auto rule_it = m_AclTables[table_oid].rules.find(rule_id); + if (rule_it == m_AclTables[table_oid].rules.end()) + { + SWSS_LOG_ERROR("Failed to update ACL rule in ACL table %s. Rule doesn't exist", rule_id.c_str()); + return false; + } + switch (aclMatchLookup[attr_name]) { case SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS: { sai_object_id_t port_oid = *(sai_object_id_t *)data; - vector in_ports = rule->getInPorts(); + vector in_ports = rule_it->second->getInPorts(); if (oper == RULE_OPER_ADD) { @@ -3004,10 +3030,14 @@ bool AclOrch::updateAclRule(shared_ptr rule, string table_id, string at attr_value += p.m_alias; attr_value += ','; } - attr_value.pop_back(); - rule->validateAddMatch(MATCH_IN_PORTS, attr_value); - m_AclTables[table_oid].rules[rule->getId()]->updateInPorts(); + if (!attr_value.empty()) + { + attr_value.pop_back(); + } + + rule_it->second->validateAddMatch(MATCH_IN_PORTS, attr_value); + rule_it->second->updateInPorts(); } break; diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index d8eac7a95a..403fb3b44a 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -440,7 +440,8 @@ class AclOrch : public Orch, public Observer bool updateAclTable(AclTable ¤tTable, AclTable &newTable); bool addAclRule(shared_ptr aclRule, string table_id); bool removeAclRule(string table_id, string rule_id); - bool updateAclRule(shared_ptr aclRule, string table_id, string attr_name, void *data, bool oper); + bool updateAclRule(string table_id, string rule_id, string attr_name, void *data, bool oper); + AclRule* getAclRule(string table_id, string rule_id); bool isCombinedMirrorV6Table(); bool isAclActionSupported(acl_stage_type_t stage, sai_acl_action_type_t action) const; diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 8e581a11ca..bb1fdceffa 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -40,8 +40,8 @@ extern sai_router_interface_api_t* sai_router_intfs_api; /* Constants */ #define MUX_TUNNEL "MuxTunnel0" -#define MUX_ACL_TABLE_NAME "mux_acl_table"; -#define MUX_ACL_RULE_NAME "mux_acl_rule"; +#define MUX_ACL_TABLE_NAME INGRESS_TABLE_DROP +#define MUX_ACL_RULE_NAME "mux_acl_rule" #define MUX_HW_STATE_UNKNOWN "unknown" #define MUX_HW_STATE_ERROR "error" @@ -202,6 +202,10 @@ static sai_object_id_t create_tunnel(const IpAddress* p_dst_ip, const IpAddress* attr.value.s32 = SAI_TUNNEL_TTL_MODE_PIPE_MODEL; tunnel_attrs.push_back(attr); + attr.id = SAI_TUNNEL_ATTR_LOOPBACK_PACKET_ACTION; + attr.value.s32 = SAI_PACKET_ACTION_DROP; + tunnel_attrs.push_back(attr); + if (p_src_ip != nullptr) { attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; @@ -343,7 +347,7 @@ bool MuxCable::stateActive() return false; } - if (!aclHandler(port.m_port_id, false)) + if (!aclHandler(port.m_port_id, mux_name_, false)) { SWSS_LOG_INFO("Remove ACL drop rule failed for %s", mux_name_.c_str()); return false; @@ -373,7 +377,7 @@ bool MuxCable::stateStandby() return false; } - if (!aclHandler(port.m_port_id)) + if (!aclHandler(port.m_port_id, mux_name_)) { SWSS_LOG_INFO("Add ACL drop rule failed for %s", mux_name_.c_str()); return false; @@ -416,6 +420,7 @@ void MuxCable::setState(string new_state) } st_chg_in_progress_ = false; + st_chg_failed_ = false; SWSS_LOG_INFO("Changed state to %s", new_state.c_str()); return; @@ -429,11 +434,11 @@ string MuxCable::getState() return (muxStateValToString.at(state_)); } -bool MuxCable::aclHandler(sai_object_id_t port, bool add) +bool MuxCable::aclHandler(sai_object_id_t port, string alias, bool add) { if (add) { - acl_handler_ = make_shared(port); + acl_handler_ = make_shared(port, alias); } else { @@ -685,16 +690,18 @@ sai_object_id_t MuxNbrHandler::getNextHopId(const NextHopKey nhKey) std::map MuxAclHandler::acl_table_; -MuxAclHandler::MuxAclHandler(sai_object_id_t port) +MuxAclHandler::MuxAclHandler(sai_object_id_t port, string alias) { SWSS_LOG_ENTER(); // There is one handler instance per MUX port - acl_table_type_t table_type = ACL_TABLE_MUX; + acl_table_type_t table_type = ACL_TABLE_DROP; string table_name = MUX_ACL_TABLE_NAME; string rule_name = MUX_ACL_RULE_NAME; port_ = port; + alias_ = alias; + auto found = acl_table_.find(table_name); if (found == acl_table_.end()) { @@ -709,8 +716,18 @@ MuxAclHandler::MuxAclHandler(sai_object_id_t port) else { SWSS_LOG_NOTICE("Binding port %" PRIx64 "", port); - // Otherwise just bind ACL table with the port - found->second.bind(port); + + AclRule* rule = gAclOrch->getAclRule(table_name, rule_name); + if (rule == nullptr) + { + shared_ptr newRule = + make_shared(gAclOrch, rule_name, table_name, table_type); + createMuxAclRule(newRule, table_name); + } + else + { + gAclOrch->updateAclRule(table_name, rule_name, MATCH_IN_PORTS, &port, RULE_OPER_ADD); + } } } @@ -718,11 +735,25 @@ MuxAclHandler::~MuxAclHandler(void) { SWSS_LOG_ENTER(); string table_name = MUX_ACL_TABLE_NAME; + string rule_name = MUX_ACL_RULE_NAME; SWSS_LOG_NOTICE("Un-Binding port %" PRIx64 "", port_); - auto found = acl_table_.find(table_name); - found->second.unbind(port_); + AclRule* rule = gAclOrch->getAclRule(table_name, rule_name); + if (rule == nullptr) + { + SWSS_LOG_THROW("ACL Rule does not exist for port %s, rule %s", alias_.c_str(), rule_name.c_str()); + } + + vector port_set = rule->getInPorts(); + if ((port_set.size() == 1) && (port_set[0] == port_)) + { + gAclOrch->removeAclRule(table_name, rule_name); + } + else + { + gAclOrch->updateAclRule(table_name, rule_name, MATCH_IN_PORTS, &port_, RULE_OPER_DELETE); + } } void MuxAclHandler::createMuxAclTable(sai_object_id_t port, string strTable) @@ -736,7 +767,17 @@ void MuxAclHandler::createMuxAclTable(sai_object_id_t port, string strTable) assert(inserted.second); AclTable& acl_table = inserted.first->second; - acl_table.type = ACL_TABLE_MUX; + + sai_object_id_t table_oid = gAclOrch->getTableById(strTable); + if (table_oid != SAI_NULL_OBJECT_ID) + { + // DROP ACL table is already created + SWSS_LOG_NOTICE("ACL table %s exists, reuse the same", strTable.c_str()); + acl_table = *(gAclOrch->getTableByOid(table_oid)); + return; + } + + acl_table.type = ACL_TABLE_DROP; acl_table.id = strTable; acl_table.link(port); acl_table.stage = ACL_STAGE_INGRESS; @@ -753,6 +794,11 @@ void MuxAclHandler::createMuxAclRule(shared_ptr rule, string strTabl attr_value = "999"; rule->validateAddPriority(attr_name, attr_value); + // Add MATCH_IN_PORTS as match criteria for ingress table + attr_name = MATCH_IN_PORTS; + attr_value = alias_; + rule->validateAddMatch(attr_name, attr_value); + attr_name = ACTION_PACKET_ACTION; attr_value = PACKET_ACTION_DROP; rule->validateAddAction(attr_name, attr_value); diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index 3264e4967a..0c34936f75 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -38,7 +38,7 @@ class MuxStateOrch; class MuxAclHandler { public: - MuxAclHandler(sai_object_id_t port); + MuxAclHandler(sai_object_id_t port, string alias); ~MuxAclHandler(void); private: @@ -48,6 +48,7 @@ class MuxAclHandler // class shared dict: ACL table name -> ACL table static std::map acl_table_; sai_object_id_t port_ = SAI_NULL_OBJECT_ID; + string alias_; }; // IP to nexthop index mapping @@ -101,7 +102,7 @@ class MuxCable bool stateInitActive(); bool stateStandby(); - bool aclHandler(sai_object_id_t, bool add = true); + bool aclHandler(sai_object_id_t port, string alias, bool add = true); bool nbrHandler(bool enable, bool update_routes = true); string mux_name_; diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 6bc65a6114..34c513e5d6 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -226,7 +226,6 @@ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, SWSS_LOG_ENTER(); acl_table_type_t table_type; - sai_object_id_t table_oid; string queuestr = to_string(queueId); m_strRule = "Rule_PfcWdAclHandler_" + queuestr; @@ -244,18 +243,15 @@ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, } else { - table_oid = gAclOrch->getTableById(m_strIngressTable); - map table_map = gAclOrch->getAclTables(); - auto rule_iter = table_map[table_oid].rules.find(m_strRule); - - if (rule_iter == table_map[table_oid].rules.end()) + AclRule* rule = gAclOrch->getAclRule(m_strIngressTable, m_strRule); + if (rule == nullptr) { shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strIngressTable, table_type); createPfcAclRule(newRule, queueId, m_strIngressTable, port); } else { - gAclOrch->updateAclRule(rule_iter->second, m_strIngressTable, MATCH_IN_PORTS, &port, RULE_OPER_ADD); + gAclOrch->updateAclRule(m_strIngressTable, m_strRule, MATCH_IN_PORTS, &port, RULE_OPER_ADD); } } @@ -281,11 +277,13 @@ PfcWdAclHandler::~PfcWdAclHandler(void) { SWSS_LOG_ENTER(); - sai_object_id_t table_oid = gAclOrch->getTableById(m_strIngressTable); - map table_map = gAclOrch->getAclTables(); - auto rule_iter = table_map[table_oid].rules.find(m_strRule); + AclRule* rule = gAclOrch->getAclRule(m_strIngressTable, m_strRule); + if (rule == nullptr) + { + SWSS_LOG_THROW("ACL Rule does not exist for rule %s", m_strRule.c_str()); + } - vector port_set = rule_iter->second->getInPorts(); + vector port_set = rule->getInPorts(); sai_object_id_t port = getPort(); if ((port_set.size() == 1) && (port_set[0] == port)) @@ -294,7 +292,7 @@ PfcWdAclHandler::~PfcWdAclHandler(void) } else { - gAclOrch->updateAclRule(rule_iter->second, m_strIngressTable, MATCH_IN_PORTS, &port, RULE_OPER_DELETE); + gAclOrch->updateAclRule(m_strIngressTable, m_strRule, MATCH_IN_PORTS, &port, RULE_OPER_DELETE); } auto found = m_aclTables.find(m_strEgressTable); @@ -323,6 +321,16 @@ void PfcWdAclHandler::createPfcAclTable(sai_object_id_t port, string strTable, b assert(inserted.second); AclTable& aclTable = inserted.first->second; + + sai_object_id_t table_oid = gAclOrch->getTableById(strTable); + if (ingress && table_oid != SAI_NULL_OBJECT_ID) + { + // DROP ACL table is already created + SWSS_LOG_NOTICE("ACL table %s exists, reuse the same", strTable.c_str()); + aclTable = *(gAclOrch->getTableByOid(table_oid)); + return; + } + aclTable.link(port); aclTable.id = strTable; diff --git a/tests/test_mux.py b/tests/test_mux.py index c1eaae75ed..c398a07656 100644 --- a/tests/test_mux.py +++ b/tests/test_mux.py @@ -29,6 +29,7 @@ class TestMuxTunnelBase(object): IPV4_MASK = "/32" IPV6_MASK = "/128" TUNNEL_NH_ID = 0 + ACL_PRIORITY = "999" ecn_modes_map = { "standard" : "SAI_TUNNEL_DECAP_ECN_MODE_STANDARD", @@ -310,6 +311,43 @@ def create_and_test_route(self, appdb, asicdb, dvs, dvs_route): self.check_nexthop_group_in_asic_db(asicdb, rtkeys[0]) + def get_expected_sai_qualifiers(self, portlist, dvs_acl): + expected_sai_qualifiers = { + "SAI_ACL_ENTRY_ATTR_PRIORITY": self.ACL_PRIORITY, + "SAI_ACL_ENTRY_ATTR_FIELD_IN_PORTS": dvs_acl.get_port_list_comparator(portlist) + } + + return expected_sai_qualifiers + + + def create_and_test_acl(self, appdb, asicdb, dvs, dvs_acl): + + # Start with active, verify NO ACL rules exists + self.set_mux_state(appdb, "Ethernet0", "active") + self.set_mux_state(appdb, "Ethernet4", "active") + + dvs_acl.verify_no_acl_rules() + + # Set one mux port to standby, verify ACL rule with inport bitmap (1 port) + self.set_mux_state(appdb, "Ethernet0", "standby") + sai_qualifier = self.get_expected_sai_qualifiers(["Ethernet0"], dvs_acl) + dvs_acl.verify_acl_rule(sai_qualifier, action="DROP", priority=self.ACL_PRIORITY) + + # Set two mux ports to standby, verify ACL rule with inport bitmap (2 ports) + self.set_mux_state(appdb, "Ethernet4", "standby") + sai_qualifier = self.get_expected_sai_qualifiers(["Ethernet0","Ethernet4"], dvs_acl) + dvs_acl.verify_acl_rule(sai_qualifier, action="DROP", priority=self.ACL_PRIORITY) + + # Set one mux port to active, verify ACL rule with inport bitmap (1 port) + self.set_mux_state(appdb, "Ethernet0", "active") + sai_qualifier = self.get_expected_sai_qualifiers(["Ethernet4"], dvs_acl) + dvs_acl.verify_acl_rule(sai_qualifier, action="DROP", priority=self.ACL_PRIORITY) + + # Set last mux port to active, verify ACL rule is deleted + self.set_mux_state(appdb, "Ethernet4", "active") + dvs_acl.verify_no_acl_rules() + + def check_interface_exists_in_asicdb(self, asicdb, sai_oid): asicdb.wait_for_entry(self.ASIC_RIF_TABLE, sai_oid) return True @@ -364,6 +402,8 @@ def create_and_test_peer(self, db, asicdb, peer_name, peer_ip, src_ip): assert self.check_interface_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_ATTR_ENCAP_TTL_MODE": assert value == "SAI_TUNNEL_TTL_MODE_PIPE_MODEL" + elif field == "SAI_TUNNEL_ATTR_LOOPBACK_PACKET_ACTION": + assert value == "SAI_PACKET_ACTION_DROP" else: assert False, "Field %s is not tested" % field @@ -539,6 +579,15 @@ def test_Route(self, dvs, dvs_route, testlog): self.create_and_test_route(appdb, asicdb, dvs, dvs_route) + def test_acl(self, dvs, dvs_acl, testlog): + """ test Route entries and mux state change """ + + appdb = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = dvs.get_asic_db() + + self.create_and_test_acl(appdb, asicdb, dvs, dvs_acl) + + # 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(): From 48dc60e8728a1ccf37e190ff28e5a8e2186851f9 Mon Sep 17 00:00:00 2001 From: Vadym Hlushko <62022266+vadymhlushko-mlnx@users.noreply.github.com> Date: Thu, 18 Mar 2021 19:21:44 +0200 Subject: [PATCH 40/54] [nbrmgrd] added function to parse IP address from APP_DB (#1672) * [nbrmgrd] added function to parse IP address from APP_DB Signed-off-by: Vadym Hlushko --- cfgmgr/nbrmgr.cpp | 24 +++++++++++++++++++++++- cfgmgr/nbrmgr.h | 2 ++ tests/test_neighbor.py | 36 +++++++++++++++++++++++++++++------- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/cfgmgr/nbrmgr.cpp b/cfgmgr/nbrmgr.cpp index fc94e77127..d5d644652e 100644 --- a/cfgmgr/nbrmgr.cpp +++ b/cfgmgr/nbrmgr.cpp @@ -205,6 +205,27 @@ bool NbrMgr::setNeighbor(const string& alias, const IpAddress& ip, const MacAddr return send_message(m_nl_sock, msg); } +/** + * Parse APPL_DB neighbors resolve table. + * + * @param [app_db_nbr_tbl_key], key from APPL_DB - APP_NEIGH_RESOLVE_TABLE_NAME + * @param [delimiter], APPL_DB delimiter ":" + * + * @return the string vector which contain the VLAN alias and IP address + */ +vector NbrMgr::parseAliasIp(const string &app_db_nbr_tbl_key, const char *delimiter) +{ + vector ret; + size_t found = app_db_nbr_tbl_key.find(delimiter); + string alias = app_db_nbr_tbl_key.substr(0, found); + string ip_address = app_db_nbr_tbl_key.substr(found + 1, app_db_nbr_tbl_key.size() - 1); + + ret.push_back(alias); + ret.push_back(ip_address); + + return ret; +} + void NbrMgr::doResolveNeighTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -213,7 +234,8 @@ void NbrMgr::doResolveNeighTask(Consumer &consumer) while (it != consumer.m_toSync.end()) { KeyOpFieldsValuesTuple t = it->second; - vector keys = tokenize(kfvKey(t),delimiter); + vector keys = parseAliasIp(kfvKey(t), consumer.getConsumerTable()->getTableNameSeparator().c_str()); + MacAddress mac; IpAddress ip(keys[1]); string alias(keys[0]); diff --git a/cfgmgr/nbrmgr.h b/cfgmgr/nbrmgr.h index d6018c18e9..73ed6b411e 100644 --- a/cfgmgr/nbrmgr.h +++ b/cfgmgr/nbrmgr.h @@ -26,6 +26,8 @@ class NbrMgr : public Orch bool isIntfStateOk(const std::string &alias); bool setNeighbor(const std::string& alias, const IpAddress& ip, const MacAddress& mac); + vector parseAliasIp(const string &app_db_nbr_tbl_key, const char *delimiter); + void doResolveNeighTask(Consumer &consumer); void doSetNeighTask(Consumer &consumer); void doTask(Consumer &consumer); diff --git a/tests/test_neighbor.py b/tests/test_neighbor.py index 140af66439..4893faeb21 100644 --- a/tests/test_neighbor.py +++ b/tests/test_neighbor.py @@ -389,10 +389,32 @@ def test_NeighborAddRemoveIpv4WithVrf(self, dvs, testlog): tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") current_neigh_entries_cnt = len(tbl.getKeys()) dec_neigh_entries_cnt = (old_neigh_entries_cnt - current_neigh_entries_cnt) - assert dec_neigh_entries_cnt == 1 - - -# 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 + assert dec_neigh_entries_cnt == 1 + + def test_FlushResolveNeighborIpv6(self, dvs, testlog): + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + prod_state_tbl = swsscommon.ProducerStateTable(appl_db, swsscommon.APP_NEIGH_RESOLVE_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([("mac", "52:54:00:25:06:E9")]) + + prod_state_tbl.set("Vlan2:2000:1::1", fvs) + time.sleep(2) + + (exitcode, output) = dvs.runcmd(['sh', '-c', "supervisorctl status nbrmgrd | awk '{print $2}'"]) + assert output == "RUNNING\n" + + def test_FlushResolveNeighborIpv4(self, dvs, testlog): + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + prod_state_tbl = swsscommon.ProducerStateTable(appl_db, swsscommon.APP_NEIGH_RESOLVE_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([("mac", "52:54:00:25:06:E9")]) + + prod_state_tbl.set("Vlan2:192.168.10.1", fvs) + time.sleep(2) + + (exitcode, output) = dvs.runcmd(['sh', '-c', "supervisorctl status nbrmgrd | awk '{print $2}'"]) + assert output == "RUNNING\n" + + +# 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 From 1382f7adb52cefb13d4de8e225dd86233a10b352 Mon Sep 17 00:00:00 2001 From: Danny Allen Date: Thu, 18 Mar 2021 14:59:41 -0700 Subject: [PATCH 41/54] [acl] Move ACL table constants to acltable.h (#1671) Signed-off-by: Danny Allen --- orchagent/aclorch.cpp | 10 +++--- orchagent/aclorch.h | 39 ++------------------- orchagent/acltable.h | 62 ++++++++++++++++++++++++--------- tests/mock_tests/aclorch_ut.cpp | 40 +++++++++------------ 4 files changed, 70 insertions(+), 81 deletions(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 02b5d47177..9c9b608a06 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -3103,11 +3103,11 @@ void AclOrch::doAclTableTask(Consumer &consumer) SWSS_LOG_DEBUG("TABLE ATTRIBUTE: %s : %s", attr_name.c_str(), attr_value.c_str()); - if (attr_name == TABLE_DESCRIPTION) + if (attr_name == ACL_TABLE_DESCRIPTION) { newTable.description = attr_value; } - else if (attr_name == TABLE_TYPE) + else if (attr_name == ACL_TABLE_TYPE) { if (!processAclTableType(attr_value, newTable.type)) { @@ -3117,7 +3117,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) break; } } - else if (attr_name == TABLE_PORTS) + else if (attr_name == ACL_TABLE_PORTS) { if (!processAclTablePorts(attr_value, newTable)) { @@ -3127,7 +3127,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) break; } } - else if (attr_name == TABLE_STAGE) + else if (attr_name == ACL_TABLE_STAGE) { if (!processAclTableStage(attr_value, newTable.stage)) { @@ -3137,7 +3137,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) break; } } - else if (attr_name == TABLE_SERVICES) + else if (attr_name == ACL_TABLE_SERVICES) { // TODO: validate control plane ACL table has this attribute continue; diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 403fb3b44a..b5f4773666 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -8,6 +8,7 @@ #include #include #include + #include "orch.h" #include "switchorch.h" #include "portsorch.h" @@ -15,29 +16,13 @@ #include "dtelorch.h" #include "observer.h" +#include "acltable.h" + // ACL counters update interval in the DB // Value is in seconds. Should not be less than 5 seconds // (in worst case update of 1265 counters takes almost 5 sec) #define COUNTERS_READ_INTERVAL 10 -#define TABLE_DESCRIPTION "POLICY_DESC" -#define TABLE_TYPE "TYPE" -#define TABLE_PORTS "PORTS" -#define TABLE_SERVICES "SERVICES" - -#define TABLE_TYPE_L3 "L3" -#define TABLE_TYPE_L3V6 "L3V6" -#define TABLE_TYPE_MIRROR "MIRROR" -#define TABLE_TYPE_MIRRORV6 "MIRRORV6" -#define TABLE_TYPE_MIRROR_DSCP "MIRROR_DSCP" -#define TABLE_TYPE_PFCWD "PFCWD" -#define TABLE_TYPE_CTRLPLANE "CTRLPLANE" -#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" -#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" -#define TABLE_TYPE_MCLAG "MCLAG" -#define TABLE_TYPE_MUX "MUX" -#define TABLE_TYPE_DROP "DROP" - #define RULE_PRIORITY "PRIORITY" #define MATCH_IN_PORTS "IN_PORTS" #define MATCH_OUT_PORTS "OUT_PORTS" @@ -109,24 +94,6 @@ #define RULE_OPER_ADD 0 #define RULE_OPER_DELETE 1 -typedef enum -{ - ACL_TABLE_UNKNOWN, - ACL_TABLE_L3, - ACL_TABLE_L3V6, - ACL_TABLE_MIRROR, - ACL_TABLE_MIRRORV6, - ACL_TABLE_MIRROR_DSCP, - ACL_TABLE_PFCWD, - ACL_TABLE_CTRLPLANE, - ACL_TABLE_DTEL_FLOW_WATCHLIST, - ACL_TABLE_DTEL_DROP_WATCHLIST, - ACL_TABLE_MCLAG, - ACL_TABLE_MUX, - ACL_TABLE_DROP -} acl_table_type_t; - -typedef map acl_table_type_lookup_t; typedef map acl_rule_attr_lookup_t; typedef map acl_ip_type_lookup_t; typedef map acl_dtel_flow_op_type_lookup_t; diff --git a/orchagent/acltable.h b/orchagent/acltable.h index 4244678843..7dd345e1e8 100644 --- a/orchagent/acltable.h +++ b/orchagent/acltable.h @@ -1,24 +1,35 @@ -#ifndef SWSS_ACLTABLE_H -#define SWSS_ACLTABLE_H +#pragma once extern "C" { #include "sai.h" } -#include #include -#include -#include - -using namespace std; - -/* TODO: move all acltable and aclrule implementation out of aclorch */ - -#define STAGE_INGRESS "INGRESS" -#define STAGE_EGRESS "EGRESS" -#define TABLE_INGRESS STAGE_INGRESS -#define TABLE_EGRESS STAGE_EGRESS -#define TABLE_STAGE "STAGE" +#include + +// TODO: Move the rest of AclTable implementation out of AclOrch + +#define ACL_TABLE_DESCRIPTION "POLICY_DESC" +#define ACL_TABLE_STAGE "STAGE" +#define ACL_TABLE_TYPE "TYPE" +#define ACL_TABLE_PORTS "PORTS" +#define ACL_TABLE_SERVICES "SERVICES" + +#define STAGE_INGRESS "INGRESS" +#define STAGE_EGRESS "EGRESS" + +#define TABLE_TYPE_L3 "L3" +#define TABLE_TYPE_L3V6 "L3V6" +#define TABLE_TYPE_MIRROR "MIRROR" +#define TABLE_TYPE_MIRRORV6 "MIRRORV6" +#define TABLE_TYPE_MIRROR_DSCP "MIRROR_DSCP" +#define TABLE_TYPE_PFCWD "PFCWD" +#define TABLE_TYPE_CTRLPLANE "CTRLPLANE" +#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" +#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" +#define TABLE_TYPE_MCLAG "MCLAG" +#define TABLE_TYPE_MUX "MUX" +#define TABLE_TYPE_DROP "DROP" typedef enum { @@ -27,6 +38,23 @@ typedef enum ACL_STAGE_EGRESS } acl_stage_type_t; -typedef map acl_stage_type_lookup_t; +typedef std::unordered_map acl_stage_type_lookup_t; -#endif /* SWSS_ACLTABLE_H */ +typedef enum +{ + ACL_TABLE_UNKNOWN, + ACL_TABLE_L3, + ACL_TABLE_L3V6, + ACL_TABLE_MIRROR, + ACL_TABLE_MIRRORV6, + ACL_TABLE_MIRROR_DSCP, + ACL_TABLE_PFCWD, + ACL_TABLE_CTRLPLANE, + ACL_TABLE_DTEL_FLOW_WATCHLIST, + ACL_TABLE_DTEL_DROP_WATCHLIST, + ACL_TABLE_MCLAG, + ACL_TABLE_MUX, + ACL_TABLE_DROP +} acl_table_type_t; + +typedef std::unordered_map acl_table_type_lookup_t; diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index cc6429ee1b..5130dec2e9 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -748,10 +748,7 @@ namespace aclorch_test { for (const auto &fv : values) { - if (fv.first == TABLE_DESCRIPTION) - { - } - else if (fv.first == TABLE_TYPE) + if (fv.first == ACL_TABLE_TYPE) { if (fv.second == TABLE_TYPE_L3) { @@ -772,16 +769,16 @@ namespace aclorch_test return false; } } - else if (fv.first == TABLE_STAGE) + else if (fv.first == ACL_TABLE_STAGE) { - if (fv.second == TABLE_INGRESS) + if (fv.second == STAGE_INGRESS) { if (acl_table.stage != ACL_STAGE_INGRESS) { return false; } } - else if (fv.second == TABLE_EGRESS) + else if (fv.second == STAGE_EGRESS) { if (acl_table.stage != ACL_STAGE_EGRESS) { @@ -793,9 +790,6 @@ namespace aclorch_test return false; } } - else if (fv.first == TABLE_PORTS) - { - } } return true; @@ -951,17 +945,17 @@ namespace aclorch_test for (const auto &acl_table_type : { TABLE_TYPE_L3, TABLE_TYPE_L3V6 }) { - for (const auto &acl_table_stage : { TABLE_INGRESS, TABLE_EGRESS }) + for (const auto &acl_table_stage : { STAGE_INGRESS, STAGE_EGRESS }) { string acl_table_id = "acl_table_1"; auto kvfAclTable = deque( { { acl_table_id, SET_COMMAND, - { { TABLE_DESCRIPTION, "filter source IP" }, - { TABLE_TYPE, acl_table_type }, - { TABLE_STAGE, acl_table_stage }, - { TABLE_PORTS, "1,2" } } } }); + { { ACL_TABLE_DESCRIPTION, "filter source IP" }, + { ACL_TABLE_TYPE, acl_table_type }, + { ACL_TABLE_STAGE, acl_table_stage }, + { ACL_TABLE_PORTS, "1,2" } } } }); // FIXME: ^^^^^^^^^^^^^ fixed port orch->doAclTableTask(kvfAclTable); @@ -1012,12 +1006,12 @@ namespace aclorch_test auto kvfAclTable = deque( { { acl_table_id, SET_COMMAND, - { { TABLE_DESCRIPTION, "filter source IP" }, - { TABLE_TYPE, TABLE_TYPE_L3 }, + { { ACL_TABLE_DESCRIPTION, "filter source IP" }, + { ACL_TABLE_TYPE, TABLE_TYPE_L3 }, // ^^^^^^^^^^^^^ L3 ACL - { TABLE_STAGE, TABLE_INGRESS }, + { ACL_TABLE_STAGE, STAGE_INGRESS }, // FIXME: ^^^^^^^^^^^^^ only support / test for ingress ? - { TABLE_PORTS, "1,2" } } } }); + { ACL_TABLE_PORTS, "1,2" } } } }); // FIXME: ^^^^^^^^^^^^^ fixed port orch->doAclTableTask(kvfAclTable); @@ -1102,12 +1096,12 @@ namespace aclorch_test auto kvfAclTable = deque( { { acl_table_id, SET_COMMAND, - { { TABLE_DESCRIPTION, "filter source IP" }, - { TABLE_TYPE, TABLE_TYPE_L3V6 }, + { { ACL_TABLE_DESCRIPTION, "filter source IP" }, + { ACL_TABLE_TYPE, TABLE_TYPE_L3V6 }, // ^^^^^^^^^^^^^ L3V6 ACL - { TABLE_STAGE, TABLE_INGRESS }, + { ACL_TABLE_STAGE, STAGE_INGRESS }, // FIXME: ^^^^^^^^^^^^^ only support / test for ingress ? - { TABLE_PORTS, "1,2" } } } }); + { ACL_TABLE_PORTS, "1,2" } } } }); // FIXME: ^^^^^^^^^^^^^ fixed port orch->doAclTableTask(kvfAclTable); From 908e0c6de6ac1b5df3a9f1da64d583a26c983f05 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Fri, 19 Mar 2021 07:18:45 +0800 Subject: [PATCH 42/54] [Dynamic buffer calc] Bug fix: Remove PGs from an administratively down port. (#1652) Remove PGs from an administratively down port. - Introduce a new state: PORT_ADMIN_DOWN which represents the port is administratively down. - Remove all PGs when the port is shut down and re-add all configured PGs when port is started up - Only record the new value but don't touch BUFFER_PG_TABLE if the following events come when a port is administratively down, a port's MTU, speed, or cable length is updated, a new PG is added to a port or an existing PG is removed from a port - Optimize the port event handling flow since refreshPriorityGroupsForPort should be called only once in case more than one fields are updated - Optimize the Lua plugin which calculates the buffer pool size according Signed-off-by: Stephen Sun stephens@nvidia.com How I verified it Run regression and vs test --- cfgmgr/buffer_pool_mellanox.lua | 73 ++++--- cfgmgr/buffermgrdyn.cpp | 331 +++++++++++++++++++++++--------- cfgmgr/buffermgrdyn.h | 7 +- tests/test_buffer_dynamic.py | 109 +++++++++-- 4 files changed, 373 insertions(+), 147 deletions(-) diff --git a/cfgmgr/buffer_pool_mellanox.lua b/cfgmgr/buffer_pool_mellanox.lua index 9adbb15a6a..bbf49367d0 100644 --- a/cfgmgr/buffer_pool_mellanox.lua +++ b/cfgmgr/buffer_pool_mellanox.lua @@ -12,7 +12,7 @@ local lossypg_400g = 0 local result = {} local profiles = {} -local count_up_port = 0 +local total_port = 0 local mgmt_pool_size = 256 * 1024 local egress_mirror_headroom = 10 * 1024 @@ -30,43 +30,38 @@ end local function iterate_all_items(all_items) table.sort(all_items) - local prev_port = "None" local port - local is_up local fvpairs - local status - local admin_down_ports = 0 for i = 1, #all_items, 1 do - -- Check whether the port on which pg or tc hosts is admin down + -- Count the number of priorities or queues in each BUFFER_PG or BUFFER_QUEUE item + -- For example, there are: + -- 3 queues in 'BUFFER_QUEUE_TABLE:Ethernet0:0-2' + -- 2 priorities in 'BUFFER_PG_TABLE:Ethernet0:3-4' port = string.match(all_items[i], "Ethernet%d+") if port ~= nil then - if prev_port ~= port then - status = redis.call('HGET', 'PORT_TABLE:'..port, 'admin_status') - prev_port = port - if status == "down" then - is_up = false - else - is_up = true - end + local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") + local profile = redis.call('HGET', all_items[i], 'profile') + local index = find_profile(profile) + if index == 0 then + -- Indicate an error in case the referenced profile hasn't been inserted or has been removed + -- It's possible when the orchagent is busy + -- The buffermgrd will take care of it and retry later + return 1 end - if is_up == true then - local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") - local profile = redis.call('HGET', all_items[i], 'profile') - local index = find_profile(profile) - local size - if string.len(range) == 1 then - size = 1 - else - size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) - end - profiles[index][2] = profiles[index][2] + size - local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') - if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then - lossypg_400g = lossypg_400g + size - end + local size + if string.len(range) == 1 then + size = 1 + else + size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) + end + profiles[index][2] = profiles[index][2] + size + local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') + if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then + lossypg_400g = lossypg_400g + size end end end + return 0 end -- Connect to CONFIG_DB @@ -74,12 +69,7 @@ redis.call('SELECT', config_db) local ports_table = redis.call('KEYS', 'PORT|*') -for i = 1, #ports_table do - local status = redis.call('HGET', ports_table[i], 'admin_status') - if status == "up" then - count_up_port = count_up_port + 1 - end -end +total_port = #ports_table local egress_lossless_pool_size = redis.call('HGET', 'BUFFER_POOL|egress_lossless_pool', 'size') @@ -114,8 +104,12 @@ end local all_pgs = redis.call('KEYS', 'BUFFER_PG*') local all_tcs = redis.call('KEYS', 'BUFFER_QUEUE*') -iterate_all_items(all_pgs) -iterate_all_items(all_tcs) +local fail_count = 0 +fail_count = fail_count + iterate_all_items(all_pgs) +fail_count = fail_count + iterate_all_items(all_tcs) +if fail_count > 0 then + return {} +end local statistics = {} @@ -130,7 +124,7 @@ for i = 1, #profiles, 1 do size = size + lossypg_reserved end if profiles[i][1] == "BUFFER_PROFILE_TABLE:egress_lossy_profile" then - profiles[i][2] = count_up_port + profiles[i][2] = total_port end if size ~= 0 then if shp_enabled and shp_size == 0 then @@ -152,7 +146,7 @@ local lossypg_extra_for_400g = (lossypg_reserved_400g - lossypg_reserved) * loss accumulative_occupied_buffer = accumulative_occupied_buffer + lossypg_extra_for_400g -- Accumulate sizes for egress mirror and management pool -local accumulative_egress_mirror_overhead = count_up_port * egress_mirror_headroom +local accumulative_egress_mirror_overhead = total_port * egress_mirror_headroom accumulative_occupied_buffer = accumulative_occupied_buffer + accumulative_egress_mirror_overhead + mgmt_pool_size -- Fetch mmu_size @@ -240,5 +234,6 @@ table.insert(result, "debug:egress_mirror:" .. accumulative_egress_mirror_overhe table.insert(result, "debug:shp_enabled:" .. tostring(shp_enabled)) table.insert(result, "debug:shp_size:" .. shp_size) table.insert(result, "debug:accumulative xoff:" .. accumulative_xoff) +table.insert(result, "debug:total port:" .. total_port) return result diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index fb00a8a779..6906e3c432 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -290,6 +290,12 @@ void BufferMgrDynamic::calculateHeadroomSize(buffer_profile_t &headroom) { auto ret = swss::runRedisScript(*m_applDb, m_headroomSha, keys, argv); + if (ret.empty()) + { + SWSS_LOG_ERROR("Failed to calculate headroom for %s", headroom.name.c_str()); + return; + } + // The format of the result: // a list of strings containing key, value pairs with colon as separator // each is a field of the profile @@ -346,6 +352,12 @@ void BufferMgrDynamic::recalculateSharedBufferPool() // 3. debug information: // debug: + if (ret.empty()) + { + SWSS_LOG_WARN("Failed to recalculate shared buffer pool size"); + return; + } + for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -647,9 +659,9 @@ void BufferMgrDynamic::releaseProfile(const string &profile_name) bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_profile_t &profile, const string &new_pg = "") { - //port: used to fetch the maximum headroom size - //profile: the profile referenced by the new_pg (if provided) or all PGs - //new_pg: which pg is newly added? + // port: used to fetch the maximum headroom size + // profile: the profile referenced by the new_pg (if provided) or all PGs + // new_pg: which pg is newly added? if (!profile.lossless) { @@ -682,6 +694,12 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ // a list of strings containing key, value pairs with colon as separator // each is the size of a buffer pool + if (ret.empty()) + { + SWSS_LOG_WARN("Failed to check headroom for %s", profile.name.c_str()); + return result; + } + for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -711,7 +729,44 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ return result; } -//Called when speed/cable length updated from CONFIG_DB +task_process_status BufferMgrDynamic::removeAllPgsFromPort(const string &port) +{ + buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; + set profilesToBeReleased; + + SWSS_LOG_INFO("Removing all PGs from port %s", port.c_str()); + + for (auto it = portPgs.begin(); it != portPgs.end(); ++it) + { + auto &key = it->first; + auto &portPg = it->second; + + SWSS_LOG_INFO("Removing PG %s from port %s", key.c_str(), port.c_str()); + + if (portPg.running_profile_name.empty()) + continue; + + m_bufferProfileLookup[portPg.running_profile_name].port_pgs.erase(key); + updateBufferPgToDb(key, portPg.running_profile_name, false); + profilesToBeReleased.insert(portPg.running_profile_name); + portPg.running_profile_name.clear(); + } + + checkSharedBufferPoolSize(); + + // Remove the old profile which is probably not referenced anymore. + if (!profilesToBeReleased.empty()) + { + for (auto &oldProfile : profilesToBeReleased) + { + releaseProfile(oldProfile); + } + } + + return task_process_status::task_success; +} + +// Called when speed/cable length updated from CONFIG_DB // Update buffer profile of a certain PG of a port or all PGs of the port according to its speed, cable_length and mtu // Called when // - port's speed, cable_length or mtu updated @@ -731,7 +786,7 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ // 2. Update port's info: speed, cable length and mtu // 3. If any of the PGs is updated, recalculate pool size // 4. try release each profile in to-be-released profile set -task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string &port, const string &speed, const string &cable_length, const string &mtu, const string &exactly_matched_key = "") +task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, const string &speed, const string &cable_length, const string &mtu, const string &exactly_matched_key = "") { port_info_t &portInfo = m_portInfoLookup[port]; string &gearbox_model = portInfo.gearbox_model; @@ -739,6 +794,12 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; set profilesToBeReleased; + if (portInfo.state == PORT_ADMIN_DOWN) + { + SWSS_LOG_INFO("Nothing to be done since the port %s is administratively down", port.c_str()); + return task_process_status::task_success; + } + // Iterate all the lossless PGs configured on this port for (auto it = portPgs.begin(); it != portPgs.end(); ++it) { @@ -752,16 +813,11 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string string newProfile, oldProfile; oldProfile = portPg.running_profile_name; - if (!oldProfile.empty()) - { - // Clear old profile - portPg.running_profile_name = ""; - } if (portPg.dynamic_calculated) { string threshold; - //Calculate new headroom size + // Calculate new headroom size if (portPg.static_configured) { // static_configured but dynamic_calculated means non-default threshold value @@ -786,8 +842,8 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string SWSS_LOG_DEBUG("Handling PG %s port %s, for static profile %s", key.c_str(), port.c_str(), newProfile.c_str()); } - //Calculate whether accumulative headroom size exceeds the maximum value - //Abort if it does + // Calculate whether accumulative headroom size exceeds the maximum value + // Abort if it does if (!isHeadroomResourceValid(port, m_bufferProfileLookup[newProfile], exactly_matched_key)) { SWSS_LOG_ERROR("Update speed (%s) and cable length (%s) for port %s failed, accumulative headroom size exceeds the limit", @@ -811,12 +867,12 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string profilesToBeReleased.insert(oldProfile); m_bufferProfileLookup[oldProfile].port_pgs.erase(key); } - } - // buffer pg needs to be updated as well - portPg.running_profile_name = newProfile; + // buffer pg needs to be updated as well + portPg.running_profile_name = newProfile; + } - //appl_db Database operation: set item BUFFER_PG|| + // appl_db Database operation: set item BUFFER_PG|| updateBufferPgToDb(key, newProfile, true); isHeadroomUpdated = true; } @@ -836,8 +892,7 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string portInfo.state = PORT_READY; - //Remove the old profile which is probably not referenced anymore. - //TODO release all profiles in to-be-removed map + // Remove the old profile which is probably not referenced anymore. if (!profilesToBeReleased.empty()) { for (auto &oldProfile : profilesToBeReleased) @@ -967,7 +1022,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const // Not having profile_name but both speed and cable length have been configured for that port // This is because the first PG on that port is configured after speed, cable length configured // Just regenerate the profile - task_status = refreshPriorityGroupsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; @@ -980,12 +1035,16 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const } else { - task_status = refreshPriorityGroupsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; } break; + case PORT_ADMIN_DOWN: + SWSS_LOG_NOTICE("Skip setting BUFFER_PG for %s because the port is administratively down", port.c_str()); + break; + default: // speed and cable length hasn't been configured // In that case, we just skip the this update and return success. @@ -997,7 +1056,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const return task_process_status::task_success; } -//Remove the currently configured lossless pg +// Remove the currently configured lossless pg task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const string &port) { auto &bufferPgs = m_portPgLookup[port]; @@ -1010,15 +1069,24 @@ task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const SWSS_LOG_NOTICE("Remove BUFFER_PG %s (profile %s, %s)", pg_key.c_str(), bufferPg.running_profile_name.c_str(), bufferPg.configured_profile_name.c_str()); - // recalculate pool size + // Recalculate pool size checkSharedBufferPoolSize(); - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) - portInfo.state = PORT_READY; - else - portInfo.state = PORT_INITIALIZING; - SWSS_LOG_NOTICE("try removing the original profile %s", bufferPg.running_profile_name.c_str()); - releaseProfile(bufferPg.running_profile_name); + if (portInfo.state != PORT_ADMIN_DOWN) + { + if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + portInfo.state = PORT_READY; + else + portInfo.state = PORT_INITIALIZING; + } + + // The bufferPg.running_profile_name can be empty if the port is admin down. + // In that case, releaseProfile should not be called + if (!bufferPg.running_profile_name.empty()) + { + SWSS_LOG_NOTICE("Try removing the original profile %s", bufferPg.running_profile_name.c_str()); + releaseProfile(bufferPg.running_profile_name); + } return task_process_status::task_success; } @@ -1043,7 +1111,7 @@ task_process_status BufferMgrDynamic::doUpdateBufferProfileForDynamicTh(buffer_p SWSS_LOG_DEBUG("Checking PG %s for dynamic profile %s", key.c_str(), profileName.c_str()); portsChecked.insert(portName); - rc = refreshPriorityGroupsForPort(portName, port.speed, port.cable_length, port.mtu); + rc = refreshPgsForPort(portName, port.speed, port.cable_length, port.mtu); if (task_process_status::task_success != rc) { SWSS_LOG_ERROR("Update the profile on %s failed", key.c_str()); @@ -1213,16 +1281,22 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to cable length updated", port.c_str()); - //Try updating the buffer information + // Try updating the buffer information switch (portInfo.state) { case PORT_INITIALIZING: portInfo.state = PORT_READY; - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); + task_status = refreshPgsForPort(port, speed, cable_length, mtu); break; case PORT_READY: - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); + task_status = refreshPgsForPort(port, speed, cable_length, mtu); + break; + + case PORT_ADMIN_DOWN: + // Nothing to be done here + SWSS_LOG_INFO("Nothing to be done when port %s's cable length updated", port.c_str()); + task_status = task_process_status::task_success; break; } @@ -1267,9 +1341,9 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu { auto &port = kfvKey(tuple); string op = kfvOp(tuple); - bool speed_updated = false, mtu_updated = false, admin_status_updated = false; + bool speed_updated = false, mtu_updated = false, admin_status_updated = false, admin_up; - SWSS_LOG_DEBUG("processing command:%s PORT table key %s", op.c_str(), port.c_str()); + SWSS_LOG_DEBUG("Processing command:%s PORT table key %s", op.c_str(), port.c_str()); port_info_t &portInfo = m_portInfoLookup[port]; @@ -1281,21 +1355,30 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu if (op == SET_COMMAND) { + string old_speed; + string old_mtu; + for (auto i : kfvFieldsValues(tuple)) { - if (fvField(i) == "speed") + if (fvField(i) == "speed" && fvValue(i) != portInfo.speed) { speed_updated = true; + old_speed = move(portInfo.speed); portInfo.speed = fvValue(i); } - else if (fvField(i) == "mtu") + + if (fvField(i) == "mtu" && fvValue(i) != portInfo.mtu) { mtu_updated = true; + old_mtu = move(portInfo.mtu); portInfo.mtu = fvValue(i); } - else if (fvField(i) == "admin_status") + + if (fvField(i) == "admin_status") { - admin_status_updated = true; + admin_up = (fvValue(i) == "up"); + auto old_admin_up = (portInfo.state != PORT_ADMIN_DOWN); + admin_status_updated = (admin_up != old_admin_up); } } @@ -1303,46 +1386,100 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu string &mtu = portInfo.mtu; string &speed = portInfo.speed; + bool need_refresh_all_pgs = false, need_remove_all_pgs = false; + if (speed_updated || mtu_updated) { - if (cable_length.empty() || speed.empty()) + if (!cable_length.empty() && !speed.empty()) { - // we still need to update pool size when port with headroom override is shut down - // even if its cable length or speed isn't configured - // so cable length and speed isn't tested for shutdown - SWSS_LOG_WARN("Cable length for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); - // We don't retry here because it doesn't make sense until the cable length is configured. - return task_process_status::task_success; + if (speed_updated) + { + if (mtu_updated) + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s and MTU updated from %s to %s", + port.c_str(), old_speed.c_str(), portInfo.speed.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + } + else + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s", + port.c_str(), old_speed.c_str(), portInfo.speed.c_str()); + } + } + else + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to MTU updated from %s to %s", + port.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + } + + // Try updating the buffer information + switch (portInfo.state) + { + case PORT_INITIALIZING: + portInfo.state = PORT_READY; + if (mtu.empty()) + { + // It's the same case as that in handleCableLenTable + mtu = DEFAULT_MTU_STR; + } + need_refresh_all_pgs = true; + break; + + case PORT_READY: + need_refresh_all_pgs = true; + break; + + case PORT_ADMIN_DOWN: + SWSS_LOG_INFO("Nothing to be done when port %s's speed or cable length updated since the port is administratively down", port.c_str()); + break; + + default: + SWSS_LOG_ERROR("Port %s: invalid port state %d when handling port update", port.c_str(), portInfo.state); + break; + } + + SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", + port.c_str(), + portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + } + else + { + SWSS_LOG_WARN("Cable length or speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); + // We don't retry here because it doesn't make sense until both cable length and speed are configured. } + } - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed or port updated", port.c_str()); + if (admin_status_updated) + { + if (admin_up) + { + if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + portInfo.state = PORT_READY; + else + portInfo.state = PORT_INITIALIZING; - //Try updating the buffer information - switch (portInfo.state) + need_refresh_all_pgs = true; + } + else { - case PORT_INITIALIZING: - portInfo.state = PORT_READY; - if (mtu.empty()) - { - // It's the same case as that in handleCableLenTable - mtu = DEFAULT_MTU_STR; - } - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); - break; + portInfo.state = PORT_ADMIN_DOWN; - case PORT_READY: - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); - break; + need_remove_all_pgs = true; } - SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", - port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + SWSS_LOG_INFO("Recalculate shared buffer pool size due to port %s's admin_status updated to %s", + port.c_str(), (admin_up ? "up" : "down")); + } + + // In case both need_remove_all_pgs and need_refresh_all_pgs are true, the need_remove_all_pgs will take effect. + // This can happen when both speed (or mtu) is changed and the admin_status is down. + // In this case, we just need record the new speed (or mtu) but don't need to refresh all PGs on the port since the port is administratively down + if (need_remove_all_pgs) + { + task_status = removeAllPgsFromPort(port); } - else if (admin_status_updated) + else if (need_refresh_all_pgs) { - SWSS_LOG_INFO("Recalculate shared buffer pool size due to port %s's admin_status updated", port.c_str()); - checkSharedBufferPoolSize(); + task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu); } } @@ -1356,7 +1493,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup string op = kfvOp(tuple); vector fvVector; - SWSS_LOG_DEBUG("processing command:%s table BUFFER_POOL key %s", op.c_str(), pool.c_str()); + SWSS_LOG_DEBUG("Processing command:%s table BUFFER_POOL key %s", op.c_str(), pool.c_str()); if (op == SET_COMMAND) { // For set command: @@ -1371,7 +1508,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup string &field = fvField(*i); string &value = fvValue(*i); - SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_size_field_name) { bufferPool.dynamic_size = false; @@ -1455,12 +1592,12 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues string op = kfvOp(tuple); vector fvVector; - SWSS_LOG_DEBUG("processing command:%s BUFFER_PROFILE table key %s", op.c_str(), profileName.c_str()); + SWSS_LOG_DEBUG("Processing command:%s BUFFER_PROFILE table key %s", op.c_str(), profileName.c_str()); if (op == SET_COMMAND) { - //For set command: - //1. Create the corresponding table entries in APPL_DB - //2. Record the table in the internal cache m_bufferProfileLookup + // For set command: + // 1. Create the corresponding table entries in APPL_DB + // 2. Record the table in the internal cache m_bufferProfileLookup buffer_profile_t &profileApp = m_bufferProfileLookup[profileName]; profileApp.static_configured = true; @@ -1472,10 +1609,10 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues } for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) { - string &field = fvField(*i); - string &value = fvValue(*i); + const string &field = fvField(*i); + string value = fvValue(*i); - SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_pool_field_name) { if (!value.empty()) @@ -1625,7 +1762,7 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, vector fvVector; buffer_pg_t &bufferPg = m_portPgLookup[port][key]; - SWSS_LOG_DEBUG("processing command:%s table BUFFER_PG key %s", op.c_str(), key.c_str()); + SWSS_LOG_DEBUG("Processing command:%s table BUFFER_PG key %s", op.c_str(), key.c_str()); if (op == SET_COMMAND) { bool ignored = false; @@ -1649,7 +1786,7 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, const string &field = fvField(*i); string value = fvValue(*i); - SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_profile_field_name && value != "NULL") { // Headroom override @@ -1692,6 +1829,13 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, bufferPg.static_configured = true; bufferPg.configured_profile_name = profileName; } + + if (field != buffer_profile_field_name) + { + SWSS_LOG_ERROR("BUFFER_PG: Invalid field %s", field.c_str()); + return task_process_status::task_invalid_entry; + } + fvVector.emplace_back(field, value); SWSS_LOG_INFO("Inserting BUFFER_PG table field %s value %s", field.c_str(), value.c_str()); } @@ -1706,16 +1850,17 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, if (!ignored && bufferPg.lossless) { doUpdatePgTask(key, port); - - if (!bufferPg.configured_profile_name.empty()) - { - m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); - } } else { SWSS_LOG_NOTICE("Inserting BUFFER_PG table entry %s into APPL_DB directly", key.c_str()); m_applBufferPgTable.set(key, fvVector); + bufferPg.running_profile_name = bufferPg.configured_profile_name; + } + + if (!bufferPg.configured_profile_name.empty()) + { + m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); } } else if (op == DEL_COMMAND) @@ -1723,12 +1868,16 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, // For del command: // 1. Removing it from APPL_DB // 2. Update internal caches - string &profileName = bufferPg.running_profile_name; + string &runningProfileName = bufferPg.running_profile_name; + string &configProfileName = bufferPg.configured_profile_name; - m_bufferProfileLookup[profileName].port_pgs.erase(key); - if (!bufferPg.configured_profile_name.empty()) + if (!runningProfileName.empty()) { - m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.erase(key); + m_bufferProfileLookup[runningProfileName].port_pgs.erase(key); + } + if (!configProfileName.empty() && configProfileName != runningProfileName) + { + m_bufferProfileLookup[configProfileName].port_pgs.erase(key); } if (bufferPg.lossless) @@ -1742,11 +1891,11 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, } m_portPgLookup[port].erase(key); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", profileName.c_str(), port.c_str(), key.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", runningProfileName.c_str(), port.c_str(), key.c_str()); if (m_portPgLookup[port].empty()) { m_portPgLookup.erase(port); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", profileName.c_str(), port.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", runningProfileName.c_str(), port.c_str()); } } else @@ -1836,7 +1985,7 @@ task_process_status BufferMgrDynamic::doBufferTableTask(KeyOpFieldsValuesTuple & string key = kfvKey(tuple); const string &name = applTable.getTableName(); - //transform the separator in key from "|" to ":" + // Transform the separator in key from "|" to ":" transformSeperator(key); string op = kfvOp(tuple); @@ -1848,7 +1997,7 @@ task_process_status BufferMgrDynamic::doBufferTableTask(KeyOpFieldsValuesTuple & for (auto i : kfvFieldsValues(tuple)) { - //transform the separator in values from "|" to ":" + // Transform the separator in values from "|" to ":" if (fvField(i) == "pool") transformReference(fvValue(i)); if (fvField(i) == "profile") diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index a5ffe39b1e..a0ddcca7b9 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -82,7 +82,9 @@ typedef enum { // Port is under initializing, which means its info hasn't been comprehensive for calculating headroom PORT_INITIALIZING, // All necessary information for calculating headroom is ready - PORT_READY + PORT_READY, + // Port is admin down. All PGs programmed to APPL_DB should be removed from the port + PORT_ADMIN_DOWN } port_state_t; typedef struct { @@ -234,7 +236,8 @@ class BufferMgrDynamic : public Orch void refreshSharedHeadroomPool(bool enable_state_updated_by_ratio, bool enable_state_updated_by_size); // Main flows - task_process_status refreshPriorityGroupsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); + task_process_status removeAllPgsFromPort(const std::string &port); + task_process_status refreshPgsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); task_process_status doUpdatePgTask(const std::string &pg_key, const std::string &port); task_process_status doRemovePgTask(const std::string &pg_key, const std::string &port); task_process_status doAdminStatusTask(const std::string port, const std::string adminStatus); diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 932247de37..61e6cbd612 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -102,10 +102,17 @@ def setup_asic_db(self, dvs): self.ingress_lossless_pool_oid = key def check_new_profile_in_asic_db(self, dvs, profile): - diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet - if len(diff) == 1: - self.newProfileInAsicDb = diff.pop() - assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {}".format(profile) + retry_count = 0 + self.newProfileInAsicDb = None + while retry_count < 5: + retry_count += 1 + diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet + if len(diff) == 1: + self.newProfileInAsicDb = diff.pop() + break + else: + time.sleep(1) + assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count) # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile) @@ -141,7 +148,10 @@ def change_cable_length(self, cable_length): def test_changeSpeed(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change speed to speed1 and verify whether the profile has been updated @@ -179,14 +189,20 @@ def test_changeSpeed(self, dvs, testlog): self.check_new_profile_in_asic_db(dvs, expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 on interface + # Remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_changeCableLen(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change to new cable length @@ -224,13 +240,19 @@ def test_changeCableLen(self, dvs, testlog): self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 on interface + # Remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_MultipleLosslessPg(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Add another lossless PG @@ -238,14 +260,14 @@ def test_MultipleLosslessPg(self, dvs, testlog): expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # change speed and check + # Change speed and check dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # change cable length and check + # Change cable length and check self.change_cable_length(self.cableLenTest1) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.cableLenTest1) @@ -254,7 +276,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # revert the speed and cable length and check + # Revert the speed and cable length and check self.change_cable_length(self.originalCableLen) dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -264,13 +286,19 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 and 6 on interface + # Remove lossless PG 3-4 and 6 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_headroomOverride(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + # Configure static profile self.config_db.update_entry('BUFFER_PROFILE', 'test', {'xon': '18432', @@ -328,7 +356,7 @@ def test_headroomOverride(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6") # readd lossless PG with dynamic profile - self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profie': 'NULL'}) + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) # remove the headroom override profile @@ -345,9 +373,15 @@ def test_headroomOverride(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_mtuUpdate(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + test_mtu = '1500' default_mtu = '9100' expectedProfileMtu = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, mtu = test_mtu) @@ -373,9 +407,15 @@ def test_mtuUpdate(self, dvs, testlog): # clear configuration self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_nonDefaultAlpha(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + test_dynamic_th_1 = '1' expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1) test_dynamic_th_2 = '2' @@ -409,12 +449,17 @@ def test_nonDefaultAlpha(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_sharedHeadroomPool(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + # configure lossless PG 3-4 on interface and start up the interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) - dvs.runcmd('config interface startup Ethernet0') expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -500,3 +545,37 @@ def test_sharedHeadroomPool(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') dvs.runcmd('config interface shutdown Ethernet0') + + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + + def test_shutdownPort(self, dvs, testlog): + self.setup_db(dvs) + + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + + # Shutdown port and check whether all the PGs have been removed + dvs.runcmd("config interface shutdown Ethernet0") + self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") + self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile) + + # Add another PG when port is administratively down + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'}) + + # Startup port and check whether all the PGs haved been added + dvs.runcmd("config interface startup Ethernet0") + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + + # Remove lossless PG 3-4 on interface + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') + + # Shutdown interface + dvs.runcmd("config interface shutdown Ethernet0") From 901211fef1cc637bdcaccbd0b39df093abc8ec6d Mon Sep 17 00:00:00 2001 From: Neetha John Date: Fri, 19 Mar 2021 20:23:43 -0700 Subject: [PATCH 43/54] Revert "[Dynamic buffer calc] Bug fix: Remove PGs from an administratively down port. (#1652)" (#1676) This reverts commit 908e0c6de6ac1b5df3a9f1da64d583a26c983f05. --- cfgmgr/buffer_pool_mellanox.lua | 73 +++---- cfgmgr/buffermgrdyn.cpp | 331 +++++++++----------------------- cfgmgr/buffermgrdyn.h | 7 +- tests/test_buffer_dynamic.py | 109 ++--------- 4 files changed, 147 insertions(+), 373 deletions(-) diff --git a/cfgmgr/buffer_pool_mellanox.lua b/cfgmgr/buffer_pool_mellanox.lua index bbf49367d0..9adbb15a6a 100644 --- a/cfgmgr/buffer_pool_mellanox.lua +++ b/cfgmgr/buffer_pool_mellanox.lua @@ -12,7 +12,7 @@ local lossypg_400g = 0 local result = {} local profiles = {} -local total_port = 0 +local count_up_port = 0 local mgmt_pool_size = 256 * 1024 local egress_mirror_headroom = 10 * 1024 @@ -30,38 +30,43 @@ end local function iterate_all_items(all_items) table.sort(all_items) + local prev_port = "None" local port + local is_up local fvpairs + local status + local admin_down_ports = 0 for i = 1, #all_items, 1 do - -- Count the number of priorities or queues in each BUFFER_PG or BUFFER_QUEUE item - -- For example, there are: - -- 3 queues in 'BUFFER_QUEUE_TABLE:Ethernet0:0-2' - -- 2 priorities in 'BUFFER_PG_TABLE:Ethernet0:3-4' + -- Check whether the port on which pg or tc hosts is admin down port = string.match(all_items[i], "Ethernet%d+") if port ~= nil then - local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") - local profile = redis.call('HGET', all_items[i], 'profile') - local index = find_profile(profile) - if index == 0 then - -- Indicate an error in case the referenced profile hasn't been inserted or has been removed - -- It's possible when the orchagent is busy - -- The buffermgrd will take care of it and retry later - return 1 - end - local size - if string.len(range) == 1 then - size = 1 - else - size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) + if prev_port ~= port then + status = redis.call('HGET', 'PORT_TABLE:'..port, 'admin_status') + prev_port = port + if status == "down" then + is_up = false + else + is_up = true + end end - profiles[index][2] = profiles[index][2] + size - local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') - if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then - lossypg_400g = lossypg_400g + size + if is_up == true then + local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") + local profile = redis.call('HGET', all_items[i], 'profile') + local index = find_profile(profile) + local size + if string.len(range) == 1 then + size = 1 + else + size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) + end + profiles[index][2] = profiles[index][2] + size + local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') + if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then + lossypg_400g = lossypg_400g + size + end end end end - return 0 end -- Connect to CONFIG_DB @@ -69,7 +74,12 @@ redis.call('SELECT', config_db) local ports_table = redis.call('KEYS', 'PORT|*') -total_port = #ports_table +for i = 1, #ports_table do + local status = redis.call('HGET', ports_table[i], 'admin_status') + if status == "up" then + count_up_port = count_up_port + 1 + end +end local egress_lossless_pool_size = redis.call('HGET', 'BUFFER_POOL|egress_lossless_pool', 'size') @@ -104,12 +114,8 @@ end local all_pgs = redis.call('KEYS', 'BUFFER_PG*') local all_tcs = redis.call('KEYS', 'BUFFER_QUEUE*') -local fail_count = 0 -fail_count = fail_count + iterate_all_items(all_pgs) -fail_count = fail_count + iterate_all_items(all_tcs) -if fail_count > 0 then - return {} -end +iterate_all_items(all_pgs) +iterate_all_items(all_tcs) local statistics = {} @@ -124,7 +130,7 @@ for i = 1, #profiles, 1 do size = size + lossypg_reserved end if profiles[i][1] == "BUFFER_PROFILE_TABLE:egress_lossy_profile" then - profiles[i][2] = total_port + profiles[i][2] = count_up_port end if size ~= 0 then if shp_enabled and shp_size == 0 then @@ -146,7 +152,7 @@ local lossypg_extra_for_400g = (lossypg_reserved_400g - lossypg_reserved) * loss accumulative_occupied_buffer = accumulative_occupied_buffer + lossypg_extra_for_400g -- Accumulate sizes for egress mirror and management pool -local accumulative_egress_mirror_overhead = total_port * egress_mirror_headroom +local accumulative_egress_mirror_overhead = count_up_port * egress_mirror_headroom accumulative_occupied_buffer = accumulative_occupied_buffer + accumulative_egress_mirror_overhead + mgmt_pool_size -- Fetch mmu_size @@ -234,6 +240,5 @@ table.insert(result, "debug:egress_mirror:" .. accumulative_egress_mirror_overhe table.insert(result, "debug:shp_enabled:" .. tostring(shp_enabled)) table.insert(result, "debug:shp_size:" .. shp_size) table.insert(result, "debug:accumulative xoff:" .. accumulative_xoff) -table.insert(result, "debug:total port:" .. total_port) return result diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 6906e3c432..fb00a8a779 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -290,12 +290,6 @@ void BufferMgrDynamic::calculateHeadroomSize(buffer_profile_t &headroom) { auto ret = swss::runRedisScript(*m_applDb, m_headroomSha, keys, argv); - if (ret.empty()) - { - SWSS_LOG_ERROR("Failed to calculate headroom for %s", headroom.name.c_str()); - return; - } - // The format of the result: // a list of strings containing key, value pairs with colon as separator // each is a field of the profile @@ -352,12 +346,6 @@ void BufferMgrDynamic::recalculateSharedBufferPool() // 3. debug information: // debug: - if (ret.empty()) - { - SWSS_LOG_WARN("Failed to recalculate shared buffer pool size"); - return; - } - for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -659,9 +647,9 @@ void BufferMgrDynamic::releaseProfile(const string &profile_name) bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_profile_t &profile, const string &new_pg = "") { - // port: used to fetch the maximum headroom size - // profile: the profile referenced by the new_pg (if provided) or all PGs - // new_pg: which pg is newly added? + //port: used to fetch the maximum headroom size + //profile: the profile referenced by the new_pg (if provided) or all PGs + //new_pg: which pg is newly added? if (!profile.lossless) { @@ -694,12 +682,6 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ // a list of strings containing key, value pairs with colon as separator // each is the size of a buffer pool - if (ret.empty()) - { - SWSS_LOG_WARN("Failed to check headroom for %s", profile.name.c_str()); - return result; - } - for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -729,44 +711,7 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ return result; } -task_process_status BufferMgrDynamic::removeAllPgsFromPort(const string &port) -{ - buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; - set profilesToBeReleased; - - SWSS_LOG_INFO("Removing all PGs from port %s", port.c_str()); - - for (auto it = portPgs.begin(); it != portPgs.end(); ++it) - { - auto &key = it->first; - auto &portPg = it->second; - - SWSS_LOG_INFO("Removing PG %s from port %s", key.c_str(), port.c_str()); - - if (portPg.running_profile_name.empty()) - continue; - - m_bufferProfileLookup[portPg.running_profile_name].port_pgs.erase(key); - updateBufferPgToDb(key, portPg.running_profile_name, false); - profilesToBeReleased.insert(portPg.running_profile_name); - portPg.running_profile_name.clear(); - } - - checkSharedBufferPoolSize(); - - // Remove the old profile which is probably not referenced anymore. - if (!profilesToBeReleased.empty()) - { - for (auto &oldProfile : profilesToBeReleased) - { - releaseProfile(oldProfile); - } - } - - return task_process_status::task_success; -} - -// Called when speed/cable length updated from CONFIG_DB +//Called when speed/cable length updated from CONFIG_DB // Update buffer profile of a certain PG of a port or all PGs of the port according to its speed, cable_length and mtu // Called when // - port's speed, cable_length or mtu updated @@ -786,7 +731,7 @@ task_process_status BufferMgrDynamic::removeAllPgsFromPort(const string &port) // 2. Update port's info: speed, cable length and mtu // 3. If any of the PGs is updated, recalculate pool size // 4. try release each profile in to-be-released profile set -task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, const string &speed, const string &cable_length, const string &mtu, const string &exactly_matched_key = "") +task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string &port, const string &speed, const string &cable_length, const string &mtu, const string &exactly_matched_key = "") { port_info_t &portInfo = m_portInfoLookup[port]; string &gearbox_model = portInfo.gearbox_model; @@ -794,12 +739,6 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; set profilesToBeReleased; - if (portInfo.state == PORT_ADMIN_DOWN) - { - SWSS_LOG_INFO("Nothing to be done since the port %s is administratively down", port.c_str()); - return task_process_status::task_success; - } - // Iterate all the lossless PGs configured on this port for (auto it = portPgs.begin(); it != portPgs.end(); ++it) { @@ -813,11 +752,16 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons string newProfile, oldProfile; oldProfile = portPg.running_profile_name; + if (!oldProfile.empty()) + { + // Clear old profile + portPg.running_profile_name = ""; + } if (portPg.dynamic_calculated) { string threshold; - // Calculate new headroom size + //Calculate new headroom size if (portPg.static_configured) { // static_configured but dynamic_calculated means non-default threshold value @@ -842,8 +786,8 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons SWSS_LOG_DEBUG("Handling PG %s port %s, for static profile %s", key.c_str(), port.c_str(), newProfile.c_str()); } - // Calculate whether accumulative headroom size exceeds the maximum value - // Abort if it does + //Calculate whether accumulative headroom size exceeds the maximum value + //Abort if it does if (!isHeadroomResourceValid(port, m_bufferProfileLookup[newProfile], exactly_matched_key)) { SWSS_LOG_ERROR("Update speed (%s) and cable length (%s) for port %s failed, accumulative headroom size exceeds the limit", @@ -867,12 +811,12 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons profilesToBeReleased.insert(oldProfile); m_bufferProfileLookup[oldProfile].port_pgs.erase(key); } - - // buffer pg needs to be updated as well - portPg.running_profile_name = newProfile; } - // appl_db Database operation: set item BUFFER_PG|| + // buffer pg needs to be updated as well + portPg.running_profile_name = newProfile; + + //appl_db Database operation: set item BUFFER_PG|| updateBufferPgToDb(key, newProfile, true); isHeadroomUpdated = true; } @@ -892,7 +836,8 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons portInfo.state = PORT_READY; - // Remove the old profile which is probably not referenced anymore. + //Remove the old profile which is probably not referenced anymore. + //TODO release all profiles in to-be-removed map if (!profilesToBeReleased.empty()) { for (auto &oldProfile : profilesToBeReleased) @@ -1022,7 +967,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const // Not having profile_name but both speed and cable length have been configured for that port // This is because the first PG on that port is configured after speed, cable length configured // Just regenerate the profile - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPriorityGroupsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; @@ -1035,16 +980,12 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const } else { - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPriorityGroupsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; } break; - case PORT_ADMIN_DOWN: - SWSS_LOG_NOTICE("Skip setting BUFFER_PG for %s because the port is administratively down", port.c_str()); - break; - default: // speed and cable length hasn't been configured // In that case, we just skip the this update and return success. @@ -1056,7 +997,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const return task_process_status::task_success; } -// Remove the currently configured lossless pg +//Remove the currently configured lossless pg task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const string &port) { auto &bufferPgs = m_portPgLookup[port]; @@ -1069,24 +1010,15 @@ task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const SWSS_LOG_NOTICE("Remove BUFFER_PG %s (profile %s, %s)", pg_key.c_str(), bufferPg.running_profile_name.c_str(), bufferPg.configured_profile_name.c_str()); - // Recalculate pool size + // recalculate pool size checkSharedBufferPoolSize(); - if (portInfo.state != PORT_ADMIN_DOWN) - { - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) - portInfo.state = PORT_READY; - else - portInfo.state = PORT_INITIALIZING; - } - - // The bufferPg.running_profile_name can be empty if the port is admin down. - // In that case, releaseProfile should not be called - if (!bufferPg.running_profile_name.empty()) - { - SWSS_LOG_NOTICE("Try removing the original profile %s", bufferPg.running_profile_name.c_str()); - releaseProfile(bufferPg.running_profile_name); - } + if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + portInfo.state = PORT_READY; + else + portInfo.state = PORT_INITIALIZING; + SWSS_LOG_NOTICE("try removing the original profile %s", bufferPg.running_profile_name.c_str()); + releaseProfile(bufferPg.running_profile_name); return task_process_status::task_success; } @@ -1111,7 +1043,7 @@ task_process_status BufferMgrDynamic::doUpdateBufferProfileForDynamicTh(buffer_p SWSS_LOG_DEBUG("Checking PG %s for dynamic profile %s", key.c_str(), profileName.c_str()); portsChecked.insert(portName); - rc = refreshPgsForPort(portName, port.speed, port.cable_length, port.mtu); + rc = refreshPriorityGroupsForPort(portName, port.speed, port.cable_length, port.mtu); if (task_process_status::task_success != rc) { SWSS_LOG_ERROR("Update the profile on %s failed", key.c_str()); @@ -1281,22 +1213,16 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to cable length updated", port.c_str()); - // Try updating the buffer information + //Try updating the buffer information switch (portInfo.state) { case PORT_INITIALIZING: portInfo.state = PORT_READY; - task_status = refreshPgsForPort(port, speed, cable_length, mtu); + task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); break; case PORT_READY: - task_status = refreshPgsForPort(port, speed, cable_length, mtu); - break; - - case PORT_ADMIN_DOWN: - // Nothing to be done here - SWSS_LOG_INFO("Nothing to be done when port %s's cable length updated", port.c_str()); - task_status = task_process_status::task_success; + task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); break; } @@ -1341,9 +1267,9 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu { auto &port = kfvKey(tuple); string op = kfvOp(tuple); - bool speed_updated = false, mtu_updated = false, admin_status_updated = false, admin_up; + bool speed_updated = false, mtu_updated = false, admin_status_updated = false; - SWSS_LOG_DEBUG("Processing command:%s PORT table key %s", op.c_str(), port.c_str()); + SWSS_LOG_DEBUG("processing command:%s PORT table key %s", op.c_str(), port.c_str()); port_info_t &portInfo = m_portInfoLookup[port]; @@ -1355,30 +1281,21 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu if (op == SET_COMMAND) { - string old_speed; - string old_mtu; - for (auto i : kfvFieldsValues(tuple)) { - if (fvField(i) == "speed" && fvValue(i) != portInfo.speed) + if (fvField(i) == "speed") { speed_updated = true; - old_speed = move(portInfo.speed); portInfo.speed = fvValue(i); } - - if (fvField(i) == "mtu" && fvValue(i) != portInfo.mtu) + else if (fvField(i) == "mtu") { mtu_updated = true; - old_mtu = move(portInfo.mtu); portInfo.mtu = fvValue(i); } - - if (fvField(i) == "admin_status") + else if (fvField(i) == "admin_status") { - admin_up = (fvValue(i) == "up"); - auto old_admin_up = (portInfo.state != PORT_ADMIN_DOWN); - admin_status_updated = (admin_up != old_admin_up); + admin_status_updated = true; } } @@ -1386,100 +1303,46 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu string &mtu = portInfo.mtu; string &speed = portInfo.speed; - bool need_refresh_all_pgs = false, need_remove_all_pgs = false; - if (speed_updated || mtu_updated) { - if (!cable_length.empty() && !speed.empty()) + if (cable_length.empty() || speed.empty()) { - if (speed_updated) - { - if (mtu_updated) - { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s and MTU updated from %s to %s", - port.c_str(), old_speed.c_str(), portInfo.speed.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); - } - else - { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s", - port.c_str(), old_speed.c_str(), portInfo.speed.c_str()); - } - } - else - { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to MTU updated from %s to %s", - port.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); - } - - // Try updating the buffer information - switch (portInfo.state) - { - case PORT_INITIALIZING: - portInfo.state = PORT_READY; - if (mtu.empty()) - { - // It's the same case as that in handleCableLenTable - mtu = DEFAULT_MTU_STR; - } - need_refresh_all_pgs = true; - break; - - case PORT_READY: - need_refresh_all_pgs = true; - break; - - case PORT_ADMIN_DOWN: - SWSS_LOG_INFO("Nothing to be done when port %s's speed or cable length updated since the port is administratively down", port.c_str()); - break; - - default: - SWSS_LOG_ERROR("Port %s: invalid port state %d when handling port update", port.c_str(), portInfo.state); - break; - } - - SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", - port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); - } - else - { - SWSS_LOG_WARN("Cable length or speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); - // We don't retry here because it doesn't make sense until both cable length and speed are configured. + // we still need to update pool size when port with headroom override is shut down + // even if its cable length or speed isn't configured + // so cable length and speed isn't tested for shutdown + SWSS_LOG_WARN("Cable length for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); + // We don't retry here because it doesn't make sense until the cable length is configured. + return task_process_status::task_success; } - } - if (admin_status_updated) - { - if (admin_up) - { - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) - portInfo.state = PORT_READY; - else - portInfo.state = PORT_INITIALIZING; + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed or port updated", port.c_str()); - need_refresh_all_pgs = true; - } - else + //Try updating the buffer information + switch (portInfo.state) { - portInfo.state = PORT_ADMIN_DOWN; + case PORT_INITIALIZING: + portInfo.state = PORT_READY; + if (mtu.empty()) + { + // It's the same case as that in handleCableLenTable + mtu = DEFAULT_MTU_STR; + } + task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); + break; - need_remove_all_pgs = true; + case PORT_READY: + task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); + break; } - SWSS_LOG_INFO("Recalculate shared buffer pool size due to port %s's admin_status updated to %s", - port.c_str(), (admin_up ? "up" : "down")); - } - - // In case both need_remove_all_pgs and need_refresh_all_pgs are true, the need_remove_all_pgs will take effect. - // This can happen when both speed (or mtu) is changed and the admin_status is down. - // In this case, we just need record the new speed (or mtu) but don't need to refresh all PGs on the port since the port is administratively down - if (need_remove_all_pgs) - { - task_status = removeAllPgsFromPort(port); + SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", + port.c_str(), + portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); } - else if (need_refresh_all_pgs) + else if (admin_status_updated) { - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu); + SWSS_LOG_INFO("Recalculate shared buffer pool size due to port %s's admin_status updated", port.c_str()); + checkSharedBufferPoolSize(); } } @@ -1493,7 +1356,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup string op = kfvOp(tuple); vector fvVector; - SWSS_LOG_DEBUG("Processing command:%s table BUFFER_POOL key %s", op.c_str(), pool.c_str()); + SWSS_LOG_DEBUG("processing command:%s table BUFFER_POOL key %s", op.c_str(), pool.c_str()); if (op == SET_COMMAND) { // For set command: @@ -1508,7 +1371,7 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup string &field = fvField(*i); string &value = fvValue(*i); - SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_size_field_name) { bufferPool.dynamic_size = false; @@ -1592,12 +1455,12 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues string op = kfvOp(tuple); vector fvVector; - SWSS_LOG_DEBUG("Processing command:%s BUFFER_PROFILE table key %s", op.c_str(), profileName.c_str()); + SWSS_LOG_DEBUG("processing command:%s BUFFER_PROFILE table key %s", op.c_str(), profileName.c_str()); if (op == SET_COMMAND) { - // For set command: - // 1. Create the corresponding table entries in APPL_DB - // 2. Record the table in the internal cache m_bufferProfileLookup + //For set command: + //1. Create the corresponding table entries in APPL_DB + //2. Record the table in the internal cache m_bufferProfileLookup buffer_profile_t &profileApp = m_bufferProfileLookup[profileName]; profileApp.static_configured = true; @@ -1609,10 +1472,10 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues } for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) { - const string &field = fvField(*i); - string value = fvValue(*i); + string &field = fvField(*i); + string &value = fvValue(*i); - SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_pool_field_name) { if (!value.empty()) @@ -1762,7 +1625,7 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, vector fvVector; buffer_pg_t &bufferPg = m_portPgLookup[port][key]; - SWSS_LOG_DEBUG("Processing command:%s table BUFFER_PG key %s", op.c_str(), key.c_str()); + SWSS_LOG_DEBUG("processing command:%s table BUFFER_PG key %s", op.c_str(), key.c_str()); if (op == SET_COMMAND) { bool ignored = false; @@ -1786,7 +1649,7 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, const string &field = fvField(*i); string value = fvValue(*i); - SWSS_LOG_DEBUG("Field:%s, value:%s", field.c_str(), value.c_str()); + SWSS_LOG_DEBUG("field:%s, value:%s", field.c_str(), value.c_str()); if (field == buffer_profile_field_name && value != "NULL") { // Headroom override @@ -1829,13 +1692,6 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, bufferPg.static_configured = true; bufferPg.configured_profile_name = profileName; } - - if (field != buffer_profile_field_name) - { - SWSS_LOG_ERROR("BUFFER_PG: Invalid field %s", field.c_str()); - return task_process_status::task_invalid_entry; - } - fvVector.emplace_back(field, value); SWSS_LOG_INFO("Inserting BUFFER_PG table field %s value %s", field.c_str(), value.c_str()); } @@ -1850,17 +1706,16 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, if (!ignored && bufferPg.lossless) { doUpdatePgTask(key, port); + + if (!bufferPg.configured_profile_name.empty()) + { + m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); + } } else { SWSS_LOG_NOTICE("Inserting BUFFER_PG table entry %s into APPL_DB directly", key.c_str()); m_applBufferPgTable.set(key, fvVector); - bufferPg.running_profile_name = bufferPg.configured_profile_name; - } - - if (!bufferPg.configured_profile_name.empty()) - { - m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); } } else if (op == DEL_COMMAND) @@ -1868,16 +1723,12 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, // For del command: // 1. Removing it from APPL_DB // 2. Update internal caches - string &runningProfileName = bufferPg.running_profile_name; - string &configProfileName = bufferPg.configured_profile_name; + string &profileName = bufferPg.running_profile_name; - if (!runningProfileName.empty()) - { - m_bufferProfileLookup[runningProfileName].port_pgs.erase(key); - } - if (!configProfileName.empty() && configProfileName != runningProfileName) + m_bufferProfileLookup[profileName].port_pgs.erase(key); + if (!bufferPg.configured_profile_name.empty()) { - m_bufferProfileLookup[configProfileName].port_pgs.erase(key); + m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.erase(key); } if (bufferPg.lossless) @@ -1891,11 +1742,11 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, } m_portPgLookup[port].erase(key); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", runningProfileName.c_str(), port.c_str(), key.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", profileName.c_str(), port.c_str(), key.c_str()); if (m_portPgLookup[port].empty()) { m_portPgLookup.erase(port); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", runningProfileName.c_str(), port.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", profileName.c_str(), port.c_str()); } } else @@ -1985,7 +1836,7 @@ task_process_status BufferMgrDynamic::doBufferTableTask(KeyOpFieldsValuesTuple & string key = kfvKey(tuple); const string &name = applTable.getTableName(); - // Transform the separator in key from "|" to ":" + //transform the separator in key from "|" to ":" transformSeperator(key); string op = kfvOp(tuple); @@ -1997,7 +1848,7 @@ task_process_status BufferMgrDynamic::doBufferTableTask(KeyOpFieldsValuesTuple & for (auto i : kfvFieldsValues(tuple)) { - // Transform the separator in values from "|" to ":" + //transform the separator in values from "|" to ":" if (fvField(i) == "pool") transformReference(fvValue(i)); if (fvField(i) == "profile") diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index a0ddcca7b9..a5ffe39b1e 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -82,9 +82,7 @@ typedef enum { // Port is under initializing, which means its info hasn't been comprehensive for calculating headroom PORT_INITIALIZING, // All necessary information for calculating headroom is ready - PORT_READY, - // Port is admin down. All PGs programmed to APPL_DB should be removed from the port - PORT_ADMIN_DOWN + PORT_READY } port_state_t; typedef struct { @@ -236,8 +234,7 @@ class BufferMgrDynamic : public Orch void refreshSharedHeadroomPool(bool enable_state_updated_by_ratio, bool enable_state_updated_by_size); // Main flows - task_process_status removeAllPgsFromPort(const std::string &port); - task_process_status refreshPgsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); + task_process_status refreshPriorityGroupsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); task_process_status doUpdatePgTask(const std::string &pg_key, const std::string &port); task_process_status doRemovePgTask(const std::string &pg_key, const std::string &port); task_process_status doAdminStatusTask(const std::string port, const std::string adminStatus); diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 61e6cbd612..932247de37 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -102,17 +102,10 @@ def setup_asic_db(self, dvs): self.ingress_lossless_pool_oid = key def check_new_profile_in_asic_db(self, dvs, profile): - retry_count = 0 - self.newProfileInAsicDb = None - while retry_count < 5: - retry_count += 1 - diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet - if len(diff) == 1: - self.newProfileInAsicDb = diff.pop() - break - else: - time.sleep(1) - assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count) + diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet + if len(diff) == 1: + self.newProfileInAsicDb = diff.pop() + assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {}".format(profile) # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile) @@ -148,10 +141,7 @@ def change_cable_length(self, cable_length): def test_changeSpeed(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - - # Configure lossless PG 3-4 on interface + # configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change speed to speed1 and verify whether the profile has been updated @@ -189,20 +179,14 @@ def test_changeSpeed(self, dvs, testlog): self.check_new_profile_in_asic_db(dvs, expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Remove lossless PG 3-4 on interface + # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_changeCableLen(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - - # Configure lossless PG 3-4 on interface + # configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change to new cable length @@ -240,19 +224,13 @@ def test_changeCableLen(self, dvs, testlog): self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Remove lossless PG 3-4 on interface + # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_MultipleLosslessPg(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - - # Configure lossless PG 3-4 on interface + # configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Add another lossless PG @@ -260,14 +238,14 @@ def test_MultipleLosslessPg(self, dvs, testlog): expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Change speed and check + # change speed and check dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Change cable length and check + # change cable length and check self.change_cable_length(self.cableLenTest1) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.cableLenTest1) @@ -276,7 +254,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Revert the speed and cable length and check + # revert the speed and cable length and check self.change_cable_length(self.originalCableLen) dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -286,19 +264,13 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # Remove lossless PG 3-4 and 6 on interface + # remove lossless PG 3-4 and 6 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_headroomOverride(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - # Configure static profile self.config_db.update_entry('BUFFER_PROFILE', 'test', {'xon': '18432', @@ -356,7 +328,7 @@ def test_headroomOverride(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6") # readd lossless PG with dynamic profile - self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profie': 'NULL'}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) # remove the headroom override profile @@ -373,15 +345,9 @@ def test_headroomOverride(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_mtuUpdate(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - test_mtu = '1500' default_mtu = '9100' expectedProfileMtu = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, mtu = test_mtu) @@ -407,15 +373,9 @@ def test_mtuUpdate(self, dvs, testlog): # clear configuration self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_nonDefaultAlpha(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - test_dynamic_th_1 = '1' expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1) test_dynamic_th_2 = '2' @@ -449,17 +409,12 @@ def test_nonDefaultAlpha(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic') - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - def test_sharedHeadroomPool(self, dvs, testlog): self.setup_db(dvs) - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - # configure lossless PG 3-4 on interface and start up the interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + dvs.runcmd('config interface startup Ethernet0') expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -545,37 +500,3 @@ def test_sharedHeadroomPool(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') dvs.runcmd('config interface shutdown Ethernet0') - - # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') - - def test_shutdownPort(self, dvs, testlog): - self.setup_db(dvs) - - # Startup interface - dvs.runcmd('config interface startup Ethernet0') - - # Configure lossless PG 3-4 on interface - self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) - expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) - self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - - # Shutdown port and check whether all the PGs have been removed - dvs.runcmd("config interface shutdown Ethernet0") - self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") - self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile) - - # Add another PG when port is administratively down - self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'}) - - # Startup port and check whether all the PGs haved been added - dvs.runcmd("config interface startup Ethernet0") - self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - - # Remove lossless PG 3-4 on interface - self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') - self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') - - # Shutdown interface - dvs.runcmd("config interface shutdown Ethernet0") From cd6ffd289b91a58859686173e9c2b7cab6fc72cd Mon Sep 17 00:00:00 2001 From: allas-nvidia <76047578+allas-nvidia@users.noreply.github.com> Date: Sun, 21 Mar 2021 09:59:56 +0200 Subject: [PATCH 44/54] reduce severity of log to info in case of flush on non-existing member (#1669) - What I did Reduced severity of log to info in case of flush on non-existing member. - Why I did it There is a race in the infra which can cause the scenario: FDB FLUSH notification is received on port which was already removed. - How I verified it > sudo config vlan add 3 > sudo config vlan member add 3 Ethernet8 > sudo config vlan member del 3 Ethernet8 Signed-off-by: allas --- orchagent/fdborch.cpp | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index dc9efb444b..7640e98032 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -168,11 +168,24 @@ void FdbOrch::update(sai_fdb_event_t type, type, update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id); + if (bridge_port_id && !m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64 ".", + if (type == SAI_FDB_EVENT_FLUSHED) + { + /* In case of flush - can be ignored due to a race. + There are notifications about FDB FLUSH (syncd/sai_redis) on port, + which was already removed by orchagent as a result of + removeVlanMember action (removeBridgePort) */ + SWSS_LOG_INFO("Flush event: Failed to get port by bridge port ID 0x%" PRIx64 ".", bridge_port_id); + + } else { + SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64 ".", + bridge_port_id); + + } return; } @@ -263,7 +276,7 @@ void FdbOrch::update(sai_fdb_event_t type, { /*port added back to vlan before we receive delete notification for flush from SAI. Re-add entry to SAI - */ + */ sai_attribute_t attr; vector attrs; @@ -286,7 +299,7 @@ void FdbOrch::update(sai_fdb_event_t type, update.add = false; if (!update.port.m_alias.empty()) - { + { update.port.m_fdb_count--; m_portsOrch->setPort(update.port.m_alias, update.port); } @@ -1036,8 +1049,8 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, attr.value.ipaddr = ipaddr; attrs.push_back(attr); } - else if (macUpdate - && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED) + else if (macUpdate + && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED) && (fdbData.origin != oldOrigin)) { /* origin is changed from Remote-advertized to Local-provisioned @@ -1051,7 +1064,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, attrs.push_back(attr); } - if (macUpdate && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED)) + if (macUpdate && (oldOrigin == FDB_ORIGIN_VXLAN_ADVERTIZED)) { if ((fdbData.origin != oldOrigin) || ((oldType == "dynamic") && (oldType != fdbData.type))) @@ -1062,7 +1075,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, } } - + if (macUpdate) { SWSS_LOG_INFO("MAC-Update FDB %s in %s on from-%s:to-%s from-%s:to-%s origin-%d-to-%d", @@ -1225,7 +1238,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) (void)m_entries.erase(entry); // Remove in StateDb - if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) { m_fdbStateTable.del(key); } @@ -1245,7 +1258,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) return true; } -void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, +void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, FdbOrigin origin, const string portName) { bool found=false; @@ -1268,7 +1281,7 @@ void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, if (iter->fdbData.origin == origin) { SWSS_LOG_INFO("FDB entry found in saved fdb. deleting..." - "mac=%s vlan_id=0x%x origin:%d port:%s", + "mac=%s vlan_id=0x%x origin:%d port:%s", mac.to_string().c_str(), vlanId, origin, itr.first.c_str()); saved_fdb_entries[itr.first].erase(iter); @@ -1280,7 +1293,7 @@ void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, { SWSS_LOG_INFO("FDB entry found in saved fdb, but Origin is " "different mac=%s vlan_id=0x%x reqOrigin:%d " - "foundOrigin:%d port:%s, IGNORED", + "foundOrigin:%d port:%s, IGNORED", mac.to_string().c_str(), vlanId, origin, iter->fdbData.origin, itr.first.c_str()); } From acfcb85d1dcaa717a0734ed9af95f36e5399e502 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Sun, 21 Mar 2021 11:06:43 -0700 Subject: [PATCH 45/54] Revert "[buffermgr] Support maximum port headroom checking (#1607)" (#1675) This reverts commit 189a9641cf47e81c7c90d2501e70b73b74b082e1. --- cfgmgr/buffer_check_headroom_mellanox.lua | 28 +++++++++++++++++++++-- orchagent/port.h | 1 - orchagent/portsorch.cpp | 23 ------------------- orchagent/portsorch.h | 3 --- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cfgmgr/buffer_check_headroom_mellanox.lua b/cfgmgr/buffer_check_headroom_mellanox.lua index 510a967b73..da4a443b1b 100644 --- a/cfgmgr/buffer_check_headroom_mellanox.lua +++ b/cfgmgr/buffer_check_headroom_mellanox.lua @@ -10,15 +10,36 @@ local new_pg = ARGV[3] local accumulative_size = 0 local appl_db = "0" +local config_db = "4" local state_db = "6" local ret_true = {} +local ret_false = {} local ret = {} local default_ret = {} table.insert(ret_true, "result:true") +table.insert(ret_false, "result:false") -default_ret = ret_true +-- Fetch the cable length from CONFIG_DB +redis.call('SELECT', config_db) +local cable_length_keys = redis.call('KEYS', 'CABLE_LENGTH*') +if #cable_length_keys == 0 then + return ret_true +end + +-- Check whether cable length exceeds 300m (maximum value in the non-dynamic-buffer solution) +local cable_length_str = redis.call('HGET', cable_length_keys[1], port) +if cable_length_str == nil then + return ret_true +end +local cable_length = tonumber(string.sub(cable_length_str, 1, -2)) +if cable_length > 300 then + default_ret = ret_false +else + default_ret = ret_true +end +table.insert(default_ret, 'debug:no max_headroom_size configured, check cable length instead') local speed = redis.call('HGET', 'PORT|' .. port, 'speed') @@ -46,6 +67,7 @@ local function get_number_of_pgs(keyname) local range = string.match(keyname, "Ethernet%d+:([^%s]+)$") local size if range == nil then + table.insert(debuginfo, "debug:invalid pg:" .. keyname) return 0 end if string.len(range) == 1 then @@ -97,9 +119,11 @@ if max_headroom_size > accumulative_size then table.insert(ret, "result:true") else table.insert(ret, "result:false") - table.insert(ret, "debug:Accumulative headroom on port " .. accumulative_size .. " exceeds the maximum available headroom which is " .. max_headroom_size) end +table.insert(ret, "debug:max headroom:" .. max_headroom_size) +table.insert(ret, "debug:accumulative headroom:" .. accumulative_size) + for i = 1, #debuginfo do table.insert(ret, debuginfo[i]) end diff --git a/orchagent/port.h b/orchagent/port.h index 35085c16c1..baf8e3046e 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -124,7 +124,6 @@ class Port uint32_t m_vnid = VNID_NONE; uint32_t m_fdb_count = 0; uint32_t m_up_member_count = 0; - uint32_t m_maximum_headroom = 0; /* * Following two bit vectors are used to lock diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 57b3df9555..749481240b 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -263,9 +263,6 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); - m_state_db = shared_ptr(new DBConnector("STATE_DB", 0)); - m_stateBufferMaximumValueTable = unique_ptr
(new Table(m_state_db.get(), STATE_BUFFER_MAXIMUM_VALUE_TABLE)); - initGearbox(); string queueWmSha, pgWmSha; @@ -3325,25 +3322,6 @@ void PortsOrch::initializePriorityGroups(Port &port) SWSS_LOG_INFO("Get priority groups for port %s", port.m_alias.c_str()); } -void PortsOrch::initializePortMaximumHeadroom(Port &port) -{ - sai_attribute_t attr; - - attr.id = SAI_PORT_ATTR_QOS_MAXIMUM_HEADROOM_SIZE; - - sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_NOTICE("Unable to get the maximum headroom for port %s rv:%d, ignored", port.m_alias.c_str(), status); - return; - } - - vector fvVector; - port.m_maximum_headroom = attr.value.u32; - fvVector.emplace_back("max_headroom_size", to_string(port.m_maximum_headroom)); - m_stateBufferMaximumValueTable->set(port.m_alias, fvVector); -} - bool PortsOrch::initializePort(Port &port) { SWSS_LOG_ENTER(); @@ -3352,7 +3330,6 @@ bool PortsOrch::initializePort(Port &port) initializePriorityGroups(port); initializeQueues(port); - initializePortMaximumHeadroom(port); /* Create host interface */ if (!addHostIntfs(port, port.m_alias, port.m_hif_id)) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 627a4f4b38..5fcbacc0cf 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -155,7 +155,6 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_pgTable; unique_ptr
m_pgPortTable; unique_ptr
m_pgIndexTable; - unique_ptr
m_stateBufferMaximumValueTable; unique_ptr m_flexCounterTable; unique_ptr m_flexCounterGroupTable; @@ -166,7 +165,6 @@ class PortsOrch : public Orch, public Subject shared_ptr m_counter_db; shared_ptr m_flex_db; - shared_ptr m_state_db; FlexCounterManager port_stat_manager; FlexCounterManager port_buffer_drop_stat_manager; @@ -229,7 +227,6 @@ class PortsOrch : public Orch, public Subject bool initializePort(Port &port); void initializePriorityGroups(Port &port); - void initializePortMaximumHeadroom(Port &port); void initializeQueues(Port &port); bool addHostIntfs(Port &port, string alias, sai_object_id_t &host_intfs_id); From a0a7e9a524a06593e2327fc8c37fb18052f524cc Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Mon, 22 Mar 2021 11:08:30 -0700 Subject: [PATCH 46/54] Deactivate mirror session only when session status is true in updateLagMember (#1666) Deactivate mirror session only when the status is true in updateLagMember --- orchagent/mirrororch.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 0495db6363..8a19df52e4 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -1342,7 +1342,10 @@ void MirrorOrch::updateLagMember(const LagMemberUpdate& update) // If LAG is empty, deactivate session if (update.lag.m_members.empty()) { - deactivateSession(name, session); + if (session.status) + { + deactivateSession(name, session); + } session.neighborInfo.portId = SAI_OBJECT_TYPE_NULL; } // Switch to a new member of the LAG From 306b9424817c159b07ec01ab47c1e01ebdfa89ba Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Mon, 22 Mar 2021 18:25:30 -0700 Subject: [PATCH 47/54] [MuxOrch] FDB ageout safety check (#1674) * FDB ageout extra check * VS test to cover fdb ageout scenario --- orchagent/muxorch.cpp | 18 ++++++++++++++++++ tests/test_mux.py | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index bb1fdceffa..db5fd7fa75 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -904,6 +904,14 @@ bool MuxOrch::isNeighborActive(const IpAddress& nbr, const MacAddress& mac, stri return ptr->isActive(); } + NextHopKey nh_key = NextHopKey(nbr, alias); + string curr_port = getNexthopMuxName(nh_key); + if (port.empty() && !curr_port.empty() && isMuxExists(curr_port)) + { + MuxCable* ptr = getMuxCable(curr_port); + return ptr->isActive(); + } + return true; } @@ -1004,6 +1012,16 @@ void MuxOrch::updateNeighbor(const NeighborUpdate& update) { /* Check if the neighbor already exists */ old_port = getNexthopMuxName(update.entry); + + /* if new port from FDB is empty or same as existing port, return and + * no further handling is required + */ + if (port.empty() || old_port == port) + { + addNexthop(update.entry, old_port); + return; + } + addNexthop(update.entry); } else diff --git a/tests/test_mux.py b/tests/test_mux.py index c398a07656..c6c3b97169 100644 --- a/tests/test_mux.py +++ b/tests/test_mux.py @@ -169,6 +169,13 @@ def add_fdb(self, dvs, port, mac): time.sleep(1) + def del_fdb(self, dvs, mac): + + appdb = dvs.get_app_db() + ps = swsscommon.ProducerStateTable(appdb.db_connection, "FDB_TABLE") + ps._del("Vlan1000:"+mac) + + time.sleep(1) def create_and_test_neighbor(self, confdb, appdb, asicdb, dvs, dvs_route): @@ -247,6 +254,23 @@ def create_and_test_fdb(self, appdb, asicdb, dvs, dvs_route): dvs_route.check_asicdb_deleted_route_entries([ip_2+self.IPV6_MASK]) self.check_neigh_in_asic_db(asicdb, ip_2, 4) + # Simulate FDB aging out test case + ip_3 = "192.168.0.200" + + self.add_neighbor(dvs, ip_3, "00:00:00:00:00:12") + + # ip_3 is added to standby mux + dvs_route.check_asicdb_route_entries([ip_3+self.IPV4_MASK]) + + # Simulate FDB age out + self.del_fdb(dvs, "00-00-00-00-00-12") + + # FDB ageout is not expected to change existing state of neighbor + dvs_route.check_asicdb_route_entries([ip_3+self.IPV4_MASK]) + + # Change to active + self.set_mux_state(appdb, "Ethernet4", "active") + dvs_route.check_asicdb_deleted_route_entries([ip_3+self.IPV4_MASK]) def create_and_test_route(self, appdb, asicdb, dvs, dvs_route): From de5fb413b82c588f0247b6c7b157eca9a3d4e849 Mon Sep 17 00:00:00 2001 From: Petro Bratash <68950226+bratashX@users.noreply.github.com> Date: Tue, 23 Mar 2021 19:02:16 +0200 Subject: [PATCH 48/54] Handle the clear request for 'Q_SHARED_ALL' (#1653) What I did Add handling the clear request for 'Q_SHARED_ALL' Why I did it In Azure/sonic-utilities#1149 added the following new commands sonic-clear queue persistent-watermark all sonic-clear queue watermark all Without these changes, commands will result in https://github.com/Azure/sonic-swss/blob/master/orchagent/watermarkorch.cpp#L221 Signed-off-by: Petro Bratash --- orchagent/watermarkorch.cpp | 22 ++++++++++++++++++---- orchagent/watermarkorch.h | 1 + tests/test_watermark.py | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/orchagent/watermarkorch.cpp b/orchagent/watermarkorch.cpp index 8b8246ce35..4d0aa80bf6 100644 --- a/orchagent/watermarkorch.cpp +++ b/orchagent/watermarkorch.cpp @@ -12,6 +12,7 @@ #define CLEAR_PG_SHARED_REQUEST "PG_SHARED" #define CLEAR_QUEUE_SHARED_UNI_REQUEST "Q_SHARED_UNI" #define CLEAR_QUEUE_SHARED_MULTI_REQUEST "Q_SHARED_MULTI" +#define CLEAR_QUEUE_SHARED_ALL_REQUEST "Q_SHARED_ALL" #define CLEAR_BUFFER_POOL_REQUEST "BUFFER_POOL" #define CLEAR_HEADROOM_POOL_REQUEST "HEADROOM_POOL" @@ -92,7 +93,7 @@ void WatermarkOrch::doTask(Consumer &consumer) void WatermarkOrch::handleWmConfigUpdate(const std::string &key, const std::vector &fvt) { - SWSS_LOG_ENTER(); + SWSS_LOG_ENTER(); if (key == "TELEMETRY_INTERVAL") { for (std::pair, std::basic_string > i: fvt) @@ -153,7 +154,7 @@ void WatermarkOrch::doTask(NotificationConsumer &consumer) init_pg_ids(); } - if (m_multicast_queue_ids.empty() and m_unicast_queue_ids.empty()) + if (m_multicast_queue_ids.empty() and m_unicast_queue_ids.empty() and m_all_queue_ids.empty()) { init_queue_ids(); } @@ -204,6 +205,12 @@ void WatermarkOrch::doTask(NotificationConsumer &consumer) "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", m_multicast_queue_ids); } + else if (data == CLEAR_QUEUE_SHARED_ALL_REQUEST) + { + clearSingleWm(table, + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", + m_all_queue_ids); + } else if (data == CLEAR_BUFFER_POOL_REQUEST) { clearSingleWm(table, @@ -232,7 +239,7 @@ void WatermarkOrch::doTask(SelectableTimer &timer) init_pg_ids(); } - if (m_multicast_queue_ids.empty() and m_unicast_queue_ids.empty()) + if (m_multicast_queue_ids.empty() and m_unicast_queue_ids.empty() and m_all_queue_ids.empty()) { init_queue_ids(); } @@ -261,6 +268,9 @@ void WatermarkOrch::doTask(SelectableTimer &timer) clearSingleWm(m_periodicWatermarkTable.get(), "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", m_multicast_queue_ids); + clearSingleWm(m_periodicWatermarkTable.get(), + "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES", + m_all_queue_ids); clearSingleWm(m_periodicWatermarkTable.get(), "SAI_BUFFER_POOL_STAT_WATERMARK_BYTES", gBufferOrch->getBufferPoolNameOidMap()); @@ -299,10 +309,14 @@ void WatermarkOrch::init_queue_ids() { m_unicast_queue_ids.push_back(id); } - else + else if (fv.second == "SAI_QUEUE_TYPE_MULTICAST") { m_multicast_queue_ids.push_back(id); } + else if (fv.second == "SAI_QUEUE_TYPE_ALL") + { + m_all_queue_ids.push_back(id); + } } } diff --git a/orchagent/watermarkorch.h b/orchagent/watermarkorch.h index dfbc6574f5..43d0939001 100644 --- a/orchagent/watermarkorch.h +++ b/orchagent/watermarkorch.h @@ -68,6 +68,7 @@ class WatermarkOrch : public Orch std::vector m_unicast_queue_ids; std::vector m_multicast_queue_ids; + std::vector m_all_queue_ids; std::vector m_pg_ids; }; diff --git a/tests/test_watermark.py b/tests/test_watermark.py index 33e4154c89..6d7c993125 100644 --- a/tests/test_watermark.py +++ b/tests/test_watermark.py @@ -159,14 +159,18 @@ def set_up(self, dvs): self.uc_q = [] self.mc_q = [] + self.all_q = [] for q in self.qs: - if self.qs.index(q) % 16 < 8: + if self.qs.index(q) % 16 < 5: tbl.set('', [(q, "SAI_QUEUE_TYPE_UNICAST")]) self.uc_q.append(q) - else: + elif self.qs.index(q) % 16 < 10: tbl.set('', [(q, "SAI_QUEUE_TYPE_MULTICAST")]) self.mc_q.append(q) + else: + tbl.set('', [(q, "SAI_QUEUE_TYPE_ALL")]) + self.all_q.append(q) def test_telemetry_period(self, dvs): self.setup_dbs(dvs) @@ -279,6 +283,30 @@ def test_clear(self, dvs): self.verify_value(dvs, self.mc_q, WmTables.persistent, SaiWmStats.queue_shared, "288") self.verify_value(dvs, self.uc_q, WmTables.user, SaiWmStats.queue_shared, "288") self.verify_value(dvs, self.mc_q, WmTables.user, SaiWmStats.queue_shared, "288") + self.verify_value(dvs, self.all_q, WmTables.user, SaiWmStats.queue_shared, "288") + self.verify_value(dvs, self.all_q, WmTables.persistent, SaiWmStats.queue_shared, "288") + + # clear queue all watermark, and verify that multicast and unicast watermarks are not affected + + # clear persistent all watermark + exitcode, output = dvs.runcmd("sonic-clear queue persistent-watermark all") + time.sleep(1) + assert exitcode == 0, "CLI failure: %s" % output + # make sure it cleared + self.verify_value(dvs, self.all_q, WmTables.persistent, SaiWmStats.queue_shared, "0") + + # clear user all watermark + exitcode, output = dvs.runcmd("sonic-clear queue watermark all") + time.sleep(1) + assert exitcode == 0, "CLI failure: %s" % output + # make sure it cleared + self.verify_value(dvs, self.all_q, WmTables.user, SaiWmStats.queue_shared, "0") + + # make sure the rest is untouched + self.verify_value(dvs, self.mc_q, WmTables.user, SaiWmStats.queue_shared, "288") + self.verify_value(dvs, self.mc_q, WmTables.persistent, SaiWmStats.queue_shared, "288") + self.verify_value(dvs, self.uc_q, WmTables.user, SaiWmStats.queue_shared, "288") + self.verify_value(dvs, self.uc_q, WmTables.persistent, SaiWmStats.queue_shared, "0") self.enable_unittests(dvs, "false") From 242189b79e97eb6587d48de1032f33ee5c6f0600 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Fri, 26 Mar 2021 08:20:28 +0800 Subject: [PATCH 49/54] [Dynamic Buffer Calc] Enhance the field checking in table handling (#1680) * Fix a typo in test_buffer_dynamic.py and enhance a field name check * Use "else if" instead of "if" Signed-off-by: Stephen Sun --- cfgmgr/buffermgrdyn.cpp | 27 +++++++++++++++++---------- tests/test_buffer_dynamic.py | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index fb00a8a779..b5b045a4fe 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -1376,15 +1376,15 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup { bufferPool.dynamic_size = false; } - if (field == buffer_pool_xoff_field_name) + else if (field == buffer_pool_xoff_field_name) { newSHPSize = value; } - if (field == buffer_pool_mode_field_name) + else if (field == buffer_pool_mode_field_name) { bufferPool.mode = value; } - if (field == buffer_pool_type_field_name) + else if (field == buffer_pool_type_field_name) { bufferPool.ingress = (value == buffer_value_ingress); } @@ -1503,32 +1503,32 @@ task_process_status BufferMgrDynamic::handleBufferProfileTable(KeyOpFieldsValues return task_process_status::task_failed; } } - if (field == buffer_xon_field_name) + else if (field == buffer_xon_field_name) { profileApp.xon = value; } - if (field == buffer_xoff_field_name) + else if (field == buffer_xoff_field_name) { profileApp.xoff = value; profileApp.lossless = true; } - if (field == buffer_xon_offset_field_name) + else if (field == buffer_xon_offset_field_name) { profileApp.xon_offset = value; } - if (field == buffer_size_field_name) + else if (field == buffer_size_field_name) { profileApp.size = value; } - if (field == buffer_dynamic_th_field_name) + else if (field == buffer_dynamic_th_field_name) { profileApp.threshold = value; } - if (field == buffer_static_th_field_name) + else if (field == buffer_static_th_field_name) { profileApp.threshold = value; } - if (field == buffer_headroom_type_field_name) + else if (field == buffer_headroom_type_field_name) { profileApp.dynamic_calculated = (value == "dynamic"); if (profileApp.dynamic_calculated) @@ -1692,6 +1692,13 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, bufferPg.static_configured = true; bufferPg.configured_profile_name = profileName; } + + if (field != buffer_profile_field_name) + { + SWSS_LOG_ERROR("BUFFER_PG: Invalid field %s", field.c_str()); + return task_process_status::task_invalid_entry; + } + fvVector.emplace_back(field, value); SWSS_LOG_INFO("Inserting BUFFER_PG table field %s value %s", field.c_str(), value.c_str()); } diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 932247de37..599c02e107 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -328,7 +328,7 @@ def test_headroomOverride(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6") # readd lossless PG with dynamic profile - self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profie': 'NULL'}) + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) # remove the headroom override profile From 5adb73eea1b8a93e9eca30eb51b9075043d5fdf0 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Thu, 25 Mar 2021 17:51:54 -0700 Subject: [PATCH 50/54] Initialize system port type variable (#1681) Initialize System port type variable to SAI_SYSTEM_PORT_TYPE_LOCAL --- orchagent/port.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchagent/port.h b/orchagent/port.h index baf8e3046e..274d2230a3 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -41,7 +41,7 @@ struct VlanInfo struct SystemPortInfo { std::string alias = ""; - sai_system_port_type_t type; + sai_system_port_type_t type = SAI_SYSTEM_PORT_TYPE_LOCAL; sai_object_id_t local_port_oid = 0; uint32_t port_id = 0; uint32_t switch_id = 0; From 89d0728401efc53c833c8fc1cc82099ac86165f1 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Fri, 26 Mar 2021 08:33:12 -0700 Subject: [PATCH 51/54] Revert "Revert "[buffermgr] Support maximum port headroom checking (#1607)" (#1675)" (#1682) This reverts commit acfcb85d1dcaa717a0734ed9af95f36e5399e502 effectively re-merging https://github.com/Azure/sonic-swss/pull/1607 --- cfgmgr/buffer_check_headroom_mellanox.lua | 28 ++--------------------- orchagent/port.h | 1 + orchagent/portsorch.cpp | 23 +++++++++++++++++++ orchagent/portsorch.h | 3 +++ 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/cfgmgr/buffer_check_headroom_mellanox.lua b/cfgmgr/buffer_check_headroom_mellanox.lua index da4a443b1b..510a967b73 100644 --- a/cfgmgr/buffer_check_headroom_mellanox.lua +++ b/cfgmgr/buffer_check_headroom_mellanox.lua @@ -10,36 +10,15 @@ local new_pg = ARGV[3] local accumulative_size = 0 local appl_db = "0" -local config_db = "4" local state_db = "6" local ret_true = {} -local ret_false = {} local ret = {} local default_ret = {} table.insert(ret_true, "result:true") -table.insert(ret_false, "result:false") --- Fetch the cable length from CONFIG_DB -redis.call('SELECT', config_db) -local cable_length_keys = redis.call('KEYS', 'CABLE_LENGTH*') -if #cable_length_keys == 0 then - return ret_true -end - --- Check whether cable length exceeds 300m (maximum value in the non-dynamic-buffer solution) -local cable_length_str = redis.call('HGET', cable_length_keys[1], port) -if cable_length_str == nil then - return ret_true -end -local cable_length = tonumber(string.sub(cable_length_str, 1, -2)) -if cable_length > 300 then - default_ret = ret_false -else - default_ret = ret_true -end -table.insert(default_ret, 'debug:no max_headroom_size configured, check cable length instead') +default_ret = ret_true local speed = redis.call('HGET', 'PORT|' .. port, 'speed') @@ -67,7 +46,6 @@ local function get_number_of_pgs(keyname) local range = string.match(keyname, "Ethernet%d+:([^%s]+)$") local size if range == nil then - table.insert(debuginfo, "debug:invalid pg:" .. keyname) return 0 end if string.len(range) == 1 then @@ -119,11 +97,9 @@ if max_headroom_size > accumulative_size then table.insert(ret, "result:true") else table.insert(ret, "result:false") + table.insert(ret, "debug:Accumulative headroom on port " .. accumulative_size .. " exceeds the maximum available headroom which is " .. max_headroom_size) end -table.insert(ret, "debug:max headroom:" .. max_headroom_size) -table.insert(ret, "debug:accumulative headroom:" .. accumulative_size) - for i = 1, #debuginfo do table.insert(ret, debuginfo[i]) end diff --git a/orchagent/port.h b/orchagent/port.h index 274d2230a3..17131b6f4b 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -124,6 +124,7 @@ class Port uint32_t m_vnid = VNID_NONE; uint32_t m_fdb_count = 0; uint32_t m_up_member_count = 0; + uint32_t m_maximum_headroom = 0; /* * Following two bit vectors are used to lock diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 749481240b..57b3df9555 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -263,6 +263,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); + m_state_db = shared_ptr(new DBConnector("STATE_DB", 0)); + m_stateBufferMaximumValueTable = unique_ptr
(new Table(m_state_db.get(), STATE_BUFFER_MAXIMUM_VALUE_TABLE)); + initGearbox(); string queueWmSha, pgWmSha; @@ -3322,6 +3325,25 @@ void PortsOrch::initializePriorityGroups(Port &port) SWSS_LOG_INFO("Get priority groups for port %s", port.m_alias.c_str()); } +void PortsOrch::initializePortMaximumHeadroom(Port &port) +{ + sai_attribute_t attr; + + attr.id = SAI_PORT_ATTR_QOS_MAXIMUM_HEADROOM_SIZE; + + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("Unable to get the maximum headroom for port %s rv:%d, ignored", port.m_alias.c_str(), status); + return; + } + + vector fvVector; + port.m_maximum_headroom = attr.value.u32; + fvVector.emplace_back("max_headroom_size", to_string(port.m_maximum_headroom)); + m_stateBufferMaximumValueTable->set(port.m_alias, fvVector); +} + bool PortsOrch::initializePort(Port &port) { SWSS_LOG_ENTER(); @@ -3330,6 +3352,7 @@ bool PortsOrch::initializePort(Port &port) initializePriorityGroups(port); initializeQueues(port); + initializePortMaximumHeadroom(port); /* Create host interface */ if (!addHostIntfs(port, port.m_alias, port.m_hif_id)) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 5fcbacc0cf..627a4f4b38 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -155,6 +155,7 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_pgTable; unique_ptr
m_pgPortTable; unique_ptr
m_pgIndexTable; + unique_ptr
m_stateBufferMaximumValueTable; unique_ptr m_flexCounterTable; unique_ptr m_flexCounterGroupTable; @@ -165,6 +166,7 @@ class PortsOrch : public Orch, public Subject shared_ptr m_counter_db; shared_ptr m_flex_db; + shared_ptr m_state_db; FlexCounterManager port_stat_manager; FlexCounterManager port_buffer_drop_stat_manager; @@ -227,6 +229,7 @@ class PortsOrch : public Orch, public Subject bool initializePort(Port &port); void initializePriorityGroups(Port &port); + void initializePortMaximumHeadroom(Port &port); void initializeQueues(Port &port); bool addHostIntfs(Port &port, string alias, sai_object_id_t &host_intfs_id); From c7a9c05b1bd54e26fee9fdcd0909379e65a18a64 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Mon, 29 Mar 2021 18:27:09 +0800 Subject: [PATCH 52/54] Stablize the test case (#1679) - What I did Stabilize the vs test. - Why I did it Stabilize the vs test. - How I verified it Run the vs test. - Details if related One logic in the vs test is to check consistency between APPL_DB and ASIC_DB for buffer profiles. However, the mapping between are stored in the orchagent and can't be accessed from outside. The way we fetch that mapping is: Get the SAI OID of all the buffer profiles in ASIC_DB at the beginning of the test, and store it to set P1 Get the SAI OID of all the buffer profiles in ASIC_DB after a new buffer profile has been created, and stored it to P2 The newly created buffer profile in ASIC_DB should be P2 - P1. However, sometimes there can be more than one OIDs in P2 - P1. This is because the old profile hasn't been removed from the ASIC, which typically caused by timing issues, which fails the test. To make the test case stable, we will retry for 5 seconds to make sure the old profile is removed and the OID of the new profile can be fetched. Signed-off-by: Stephen Sun --- tests/test_buffer_dynamic.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 599c02e107..2c16aeac1f 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -102,10 +102,17 @@ def setup_asic_db(self, dvs): self.ingress_lossless_pool_oid = key def check_new_profile_in_asic_db(self, dvs, profile): - diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet - if len(diff) == 1: - self.newProfileInAsicDb = diff.pop() - assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {}".format(profile) + retry_count = 0 + self.newProfileInAsicDb = None + while retry_count < 5: + retry_count += 1 + diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.initProfileSet + if len(diff) == 1: + self.newProfileInAsicDb = diff.pop() + break + else: + time.sleep(1) + assert self.newProfileInAsicDb, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count) # in case diff is empty, we just treat the newProfileInAsicDb cached the latest one fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", profile) From 8eb232650f9aa74fc51f375d1bb831750deaeb9d Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Mon, 29 Mar 2021 18:28:41 +0800 Subject: [PATCH 53/54] Remove PGs from an administratively down port. (#1677) - What I did Bug fixes: Remove PGs from an administratively down port. - Why I did it To fix bugs - How I verified it Run regression and vs test - Details if related Remove PGs from an administratively down port. Introduce a new state: PORT_ADMIN_DOWN which represents the port is administratively down. Remove all PGs when the port is shut down and re-add all configured PGs when port is started up. Only record the new value but don't touch BUFFER_PG_TABLE if the following events come when a port is administratively down a port's MTU, speed, or cable length is updated a new PG is added to a port or an existing PG is removed from a port. Optimize the port event handling flow since refreshPriorityGroupsForPort should be called only once in case more than one fields are updated. Adjust the Lua plugin which calculates the buffer pool size accordingly Signed-off-by: Stephen Sun --- cfgmgr/buffer_pool_mellanox.lua | 73 ++++----- cfgmgr/buffermgrdyn.cpp | 274 ++++++++++++++++++++++++-------- cfgmgr/buffermgrdyn.h | 5 +- tests/test_buffer_dynamic.py | 92 +++++++++-- 4 files changed, 328 insertions(+), 116 deletions(-) diff --git a/cfgmgr/buffer_pool_mellanox.lua b/cfgmgr/buffer_pool_mellanox.lua index 9adbb15a6a..bbf49367d0 100644 --- a/cfgmgr/buffer_pool_mellanox.lua +++ b/cfgmgr/buffer_pool_mellanox.lua @@ -12,7 +12,7 @@ local lossypg_400g = 0 local result = {} local profiles = {} -local count_up_port = 0 +local total_port = 0 local mgmt_pool_size = 256 * 1024 local egress_mirror_headroom = 10 * 1024 @@ -30,43 +30,38 @@ end local function iterate_all_items(all_items) table.sort(all_items) - local prev_port = "None" local port - local is_up local fvpairs - local status - local admin_down_ports = 0 for i = 1, #all_items, 1 do - -- Check whether the port on which pg or tc hosts is admin down + -- Count the number of priorities or queues in each BUFFER_PG or BUFFER_QUEUE item + -- For example, there are: + -- 3 queues in 'BUFFER_QUEUE_TABLE:Ethernet0:0-2' + -- 2 priorities in 'BUFFER_PG_TABLE:Ethernet0:3-4' port = string.match(all_items[i], "Ethernet%d+") if port ~= nil then - if prev_port ~= port then - status = redis.call('HGET', 'PORT_TABLE:'..port, 'admin_status') - prev_port = port - if status == "down" then - is_up = false - else - is_up = true - end + local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") + local profile = redis.call('HGET', all_items[i], 'profile') + local index = find_profile(profile) + if index == 0 then + -- Indicate an error in case the referenced profile hasn't been inserted or has been removed + -- It's possible when the orchagent is busy + -- The buffermgrd will take care of it and retry later + return 1 end - if is_up == true then - local range = string.match(all_items[i], "Ethernet%d+:([^%s]+)$") - local profile = redis.call('HGET', all_items[i], 'profile') - local index = find_profile(profile) - local size - if string.len(range) == 1 then - size = 1 - else - size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) - end - profiles[index][2] = profiles[index][2] + size - local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') - if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then - lossypg_400g = lossypg_400g + size - end + local size + if string.len(range) == 1 then + size = 1 + else + size = 1 + tonumber(string.sub(range, -1)) - tonumber(string.sub(range, 1, 1)) + end + profiles[index][2] = profiles[index][2] + size + local speed = redis.call('HGET', 'PORT_TABLE:'..port, 'speed') + if speed == '400000' and profile == '[BUFFER_PROFILE_TABLE:ingress_lossy_profile]' then + lossypg_400g = lossypg_400g + size end end end + return 0 end -- Connect to CONFIG_DB @@ -74,12 +69,7 @@ redis.call('SELECT', config_db) local ports_table = redis.call('KEYS', 'PORT|*') -for i = 1, #ports_table do - local status = redis.call('HGET', ports_table[i], 'admin_status') - if status == "up" then - count_up_port = count_up_port + 1 - end -end +total_port = #ports_table local egress_lossless_pool_size = redis.call('HGET', 'BUFFER_POOL|egress_lossless_pool', 'size') @@ -114,8 +104,12 @@ end local all_pgs = redis.call('KEYS', 'BUFFER_PG*') local all_tcs = redis.call('KEYS', 'BUFFER_QUEUE*') -iterate_all_items(all_pgs) -iterate_all_items(all_tcs) +local fail_count = 0 +fail_count = fail_count + iterate_all_items(all_pgs) +fail_count = fail_count + iterate_all_items(all_tcs) +if fail_count > 0 then + return {} +end local statistics = {} @@ -130,7 +124,7 @@ for i = 1, #profiles, 1 do size = size + lossypg_reserved end if profiles[i][1] == "BUFFER_PROFILE_TABLE:egress_lossy_profile" then - profiles[i][2] = count_up_port + profiles[i][2] = total_port end if size ~= 0 then if shp_enabled and shp_size == 0 then @@ -152,7 +146,7 @@ local lossypg_extra_for_400g = (lossypg_reserved_400g - lossypg_reserved) * loss accumulative_occupied_buffer = accumulative_occupied_buffer + lossypg_extra_for_400g -- Accumulate sizes for egress mirror and management pool -local accumulative_egress_mirror_overhead = count_up_port * egress_mirror_headroom +local accumulative_egress_mirror_overhead = total_port * egress_mirror_headroom accumulative_occupied_buffer = accumulative_occupied_buffer + accumulative_egress_mirror_overhead + mgmt_pool_size -- Fetch mmu_size @@ -240,5 +234,6 @@ table.insert(result, "debug:egress_mirror:" .. accumulative_egress_mirror_overhe table.insert(result, "debug:shp_enabled:" .. tostring(shp_enabled)) table.insert(result, "debug:shp_size:" .. shp_size) table.insert(result, "debug:accumulative xoff:" .. accumulative_xoff) +table.insert(result, "debug:total port:" .. total_port) return result diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index b5b045a4fe..8ef577cc11 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -290,6 +290,12 @@ void BufferMgrDynamic::calculateHeadroomSize(buffer_profile_t &headroom) { auto ret = swss::runRedisScript(*m_applDb, m_headroomSha, keys, argv); + if (ret.empty()) + { + SWSS_LOG_WARN("Failed to calculate headroom for %s", headroom.name.c_str()); + return; + } + // The format of the result: // a list of strings containing key, value pairs with colon as separator // each is a field of the profile @@ -346,6 +352,12 @@ void BufferMgrDynamic::recalculateSharedBufferPool() // 3. debug information: // debug: + if (ret.empty()) + { + SWSS_LOG_WARN("Failed to recalculate the shared buffer pool size"); + return; + } + for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -682,6 +694,12 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ // a list of strings containing key, value pairs with colon as separator // each is the size of a buffer pool + if (ret.empty()) + { + SWSS_LOG_WARN("Failed to check headroom for %s", profile.name.c_str()); + return result; + } + for ( auto i : ret) { auto pairs = tokenize(i, ':'); @@ -711,7 +729,44 @@ bool BufferMgrDynamic::isHeadroomResourceValid(const string &port, const buffer_ return result; } -//Called when speed/cable length updated from CONFIG_DB +task_process_status BufferMgrDynamic::removeAllPgsFromPort(const string &port) +{ + buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; + set profilesToBeReleased; + + SWSS_LOG_INFO("Removing all PGs from port %s", port.c_str()); + + for (auto it = portPgs.begin(); it != portPgs.end(); ++it) + { + auto &key = it->first; + auto &portPg = it->second; + + SWSS_LOG_INFO("Removing PG %s from port %s", key.c_str(), port.c_str()); + + if (portPg.running_profile_name.empty()) + continue; + + m_bufferProfileLookup[portPg.running_profile_name].port_pgs.erase(key); + updateBufferPgToDb(key, portPg.running_profile_name, false); + profilesToBeReleased.insert(portPg.running_profile_name); + portPg.running_profile_name.clear(); + } + + checkSharedBufferPoolSize(); + + // Remove the old profile which is probably not referenced anymore. + if (!profilesToBeReleased.empty()) + { + for (auto &oldProfile : profilesToBeReleased) + { + releaseProfile(oldProfile); + } + } + + return task_process_status::task_success; +} + +// Called when speed/cable length updated from CONFIG_DB // Update buffer profile of a certain PG of a port or all PGs of the port according to its speed, cable_length and mtu // Called when // - port's speed, cable_length or mtu updated @@ -739,6 +794,12 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string buffer_pg_lookup_t &portPgs = m_portPgLookup[port]; set profilesToBeReleased; + if (portInfo.state == PORT_ADMIN_DOWN) + { + SWSS_LOG_INFO("Nothing to be done since the port %s is administratively down", port.c_str()); + return task_process_status::task_success; + } + // Iterate all the lossless PGs configured on this port for (auto it = portPgs.begin(); it != portPgs.end(); ++it) { @@ -752,16 +813,11 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string string newProfile, oldProfile; oldProfile = portPg.running_profile_name; - if (!oldProfile.empty()) - { - // Clear old profile - portPg.running_profile_name = ""; - } if (portPg.dynamic_calculated) { string threshold; - //Calculate new headroom size + // Calculate new headroom size if (portPg.static_configured) { // static_configured but dynamic_calculated means non-default threshold value @@ -786,8 +842,8 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string SWSS_LOG_DEBUG("Handling PG %s port %s, for static profile %s", key.c_str(), port.c_str(), newProfile.c_str()); } - //Calculate whether accumulative headroom size exceeds the maximum value - //Abort if it does + // Calculate whether accumulative headroom size exceeds the maximum value + // Abort if it does if (!isHeadroomResourceValid(port, m_bufferProfileLookup[newProfile], exactly_matched_key)) { SWSS_LOG_ERROR("Update speed (%s) and cable length (%s) for port %s failed, accumulative headroom size exceeds the limit", @@ -811,12 +867,12 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string profilesToBeReleased.insert(oldProfile); m_bufferProfileLookup[oldProfile].port_pgs.erase(key); } - } - // buffer pg needs to be updated as well - portPg.running_profile_name = newProfile; + // buffer pg needs to be updated as well + portPg.running_profile_name = newProfile; + } - //appl_db Database operation: set item BUFFER_PG|| + // appl_db Database operation: set item BUFFER_PG|| updateBufferPgToDb(key, newProfile, true); isHeadroomUpdated = true; } @@ -836,8 +892,7 @@ task_process_status BufferMgrDynamic::refreshPriorityGroupsForPort(const string portInfo.state = PORT_READY; - //Remove the old profile which is probably not referenced anymore. - //TODO release all profiles in to-be-removed map + // Remove the old profile which is probably not referenced anymore. if (!profilesToBeReleased.empty()) { for (auto &oldProfile : profilesToBeReleased) @@ -986,6 +1041,10 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const } break; + case PORT_ADMIN_DOWN: + SWSS_LOG_NOTICE("Skip setting BUFFER_PG for %s because the port is administratively down", port.c_str()); + break; + default: // speed and cable length hasn't been configured // In that case, we just skip the this update and return success. @@ -1010,15 +1069,24 @@ task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const SWSS_LOG_NOTICE("Remove BUFFER_PG %s (profile %s, %s)", pg_key.c_str(), bufferPg.running_profile_name.c_str(), bufferPg.configured_profile_name.c_str()); - // recalculate pool size + // Recalculate pool size checkSharedBufferPoolSize(); - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) - portInfo.state = PORT_READY; - else - portInfo.state = PORT_INITIALIZING; - SWSS_LOG_NOTICE("try removing the original profile %s", bufferPg.running_profile_name.c_str()); - releaseProfile(bufferPg.running_profile_name); + if (portInfo.state != PORT_ADMIN_DOWN) + { + if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + portInfo.state = PORT_READY; + else + portInfo.state = PORT_INITIALIZING; + } + + // The bufferPg.running_profile_name can be empty if the port is admin down. + // In that case, releaseProfile should not be called + if (!bufferPg.running_profile_name.empty()) + { + SWSS_LOG_NOTICE("Try removing the original profile %s", bufferPg.running_profile_name.c_str()); + releaseProfile(bufferPg.running_profile_name); + } return task_process_status::task_success; } @@ -1224,6 +1292,12 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple case PORT_READY: task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); break; + + case PORT_ADMIN_DOWN: + // Nothing to be done here + SWSS_LOG_INFO("Nothing to be done when port %s's cable length updated", port.c_str()); + task_status = task_process_status::task_success; + break; } switch (task_status) @@ -1267,7 +1341,7 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu { auto &port = kfvKey(tuple); string op = kfvOp(tuple); - bool speed_updated = false, mtu_updated = false, admin_status_updated = false; + bool speed_updated = false, mtu_updated = false, admin_status_updated = false, admin_up; SWSS_LOG_DEBUG("processing command:%s PORT table key %s", op.c_str(), port.c_str()); @@ -1281,21 +1355,30 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu if (op == SET_COMMAND) { + string old_speed; + string old_mtu; + for (auto i : kfvFieldsValues(tuple)) { - if (fvField(i) == "speed") + if (fvField(i) == "speed" && fvValue(i) != portInfo.speed) { speed_updated = true; + old_speed = move(portInfo.speed); portInfo.speed = fvValue(i); } - else if (fvField(i) == "mtu") + + if (fvField(i) == "mtu" && fvValue(i) != portInfo.mtu) { mtu_updated = true; + old_mtu = move(portInfo.mtu); portInfo.mtu = fvValue(i); } - else if (fvField(i) == "admin_status") + + if (fvField(i) == "admin_status") { - admin_status_updated = true; + admin_up = (fvValue(i) == "up"); + auto old_admin_up = (portInfo.state != PORT_ADMIN_DOWN); + admin_status_updated = (admin_up != old_admin_up); } } @@ -1303,46 +1386,100 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu string &mtu = portInfo.mtu; string &speed = portInfo.speed; + bool need_refresh_all_pgs = false, need_remove_all_pgs = false; + if (speed_updated || mtu_updated) { - if (cable_length.empty() || speed.empty()) + if (!cable_length.empty() && !speed.empty()) { - // we still need to update pool size when port with headroom override is shut down - // even if its cable length or speed isn't configured - // so cable length and speed isn't tested for shutdown - SWSS_LOG_WARN("Cable length for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); - // We don't retry here because it doesn't make sense until the cable length is configured. - return task_process_status::task_success; - } - - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed or port updated", port.c_str()); + if (speed_updated) + { + if (mtu_updated) + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s and MTU updated from %s to %s", + port.c_str(), old_speed.c_str(), portInfo.speed.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + } + else + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s", + port.c_str(), old_speed.c_str(), portInfo.speed.c_str()); + } + } + else + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to MTU updated from %s to %s", + port.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + } - //Try updating the buffer information - switch (portInfo.state) - { - case PORT_INITIALIZING: - portInfo.state = PORT_READY; - if (mtu.empty()) + // Try updating the buffer information + switch (portInfo.state) { - // It's the same case as that in handleCableLenTable - mtu = DEFAULT_MTU_STR; + case PORT_INITIALIZING: + portInfo.state = PORT_READY; + if (mtu.empty()) + { + // It's the same case as that in handleCableLenTable + mtu = DEFAULT_MTU_STR; + } + need_refresh_all_pgs = true; + break; + + case PORT_READY: + need_refresh_all_pgs = true; + break; + + case PORT_ADMIN_DOWN: + SWSS_LOG_INFO("Nothing to be done when port %s's speed or cable length updated since the port is administratively down", port.c_str()); + break; + + default: + SWSS_LOG_ERROR("Port %s: invalid port state %d when handling port update", port.c_str(), portInfo.state); + break; } - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); - break; - case PORT_READY: - task_status = refreshPriorityGroupsForPort(port, speed, cable_length, mtu); - break; + SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", + port.c_str(), + portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + } + else + { + SWSS_LOG_WARN("Cable length or speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); + // We don't retry here because it doesn't make sense until both cable length and speed are configured. } - SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", - port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); } - else if (admin_status_updated) + + if (admin_status_updated) { + if (admin_up) + { + if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + portInfo.state = PORT_READY; + else + portInfo.state = PORT_INITIALIZING; + + need_refresh_all_pgs = true; + } + else + { + portInfo.state = PORT_ADMIN_DOWN; + + need_remove_all_pgs = true; + } + SWSS_LOG_INFO("Recalculate shared buffer pool size due to port %s's admin_status updated", port.c_str()); - checkSharedBufferPoolSize(); + } + + // In case both need_remove_all_pgs and need_refresh_all_pgs are true, the need_remove_all_pgs will take effect. + // This can happen when both speed (or mtu) is changed and the admin_status is down. + // In this case, we just need record the new speed (or mtu) but don't need to refresh all PGs on the port since the port is administratively down + if (need_remove_all_pgs) + { + task_status = removeAllPgsFromPort(port); + } + else if (need_refresh_all_pgs) + { + task_status = refreshPriorityGroupsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu); } } @@ -1713,16 +1850,17 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, if (!ignored && bufferPg.lossless) { doUpdatePgTask(key, port); - - if (!bufferPg.configured_profile_name.empty()) - { - m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); - } } else { SWSS_LOG_NOTICE("Inserting BUFFER_PG table entry %s into APPL_DB directly", key.c_str()); m_applBufferPgTable.set(key, fvVector); + bufferPg.running_profile_name = bufferPg.configured_profile_name; + } + + if (!bufferPg.configured_profile_name.empty()) + { + m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.insert(key); } } else if (op == DEL_COMMAND) @@ -1730,12 +1868,16 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, // For del command: // 1. Removing it from APPL_DB // 2. Update internal caches - string &profileName = bufferPg.running_profile_name; + string &runningProfileName = bufferPg.running_profile_name; + string &configProfileName = bufferPg.configured_profile_name; - m_bufferProfileLookup[profileName].port_pgs.erase(key); - if (!bufferPg.configured_profile_name.empty()) + if (!runningProfileName.empty()) { - m_bufferProfileLookup[bufferPg.configured_profile_name].port_pgs.erase(key); + m_bufferProfileLookup[runningProfileName].port_pgs.erase(key); + } + if (!configProfileName.empty() && configProfileName != runningProfileName) + { + m_bufferProfileLookup[configProfileName].port_pgs.erase(key); } if (bufferPg.lossless) @@ -1749,11 +1891,11 @@ task_process_status BufferMgrDynamic::handleOneBufferPgEntry(const string &key, } m_portPgLookup[port].erase(key); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", profileName.c_str(), port.c_str(), key.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s PG %s", runningProfileName.c_str(), port.c_str(), key.c_str()); if (m_portPgLookup[port].empty()) { m_portPgLookup.erase(port); - SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", profileName.c_str(), port.c_str()); + SWSS_LOG_DEBUG("Profile %s has been removed from port %s on all lossless PG", runningProfileName.c_str(), port.c_str()); } } else diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index a5ffe39b1e..a74033a719 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -82,7 +82,9 @@ typedef enum { // Port is under initializing, which means its info hasn't been comprehensive for calculating headroom PORT_INITIALIZING, // All necessary information for calculating headroom is ready - PORT_READY + PORT_READY, + // Port is admin down. All PGs programmed to APPL_DB should be removed from the port + PORT_ADMIN_DOWN } port_state_t; typedef struct { @@ -234,6 +236,7 @@ class BufferMgrDynamic : public Orch void refreshSharedHeadroomPool(bool enable_state_updated_by_ratio, bool enable_state_updated_by_size); // Main flows + task_process_status removeAllPgsFromPort(const std::string &port); task_process_status refreshPriorityGroupsForPort(const std::string &port, const std::string &speed, const std::string &cable_length, const std::string &mtu, const std::string &exactly_matched_key); task_process_status doUpdatePgTask(const std::string &pg_key, const std::string &port); task_process_status doRemovePgTask(const std::string &pg_key, const std::string &port); diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 2c16aeac1f..61e6cbd612 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -148,7 +148,10 @@ def change_cable_length(self, cable_length): def test_changeSpeed(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change speed to speed1 and verify whether the profile has been updated @@ -186,14 +189,20 @@ def test_changeSpeed(self, dvs, testlog): self.check_new_profile_in_asic_db(dvs, expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 on interface + # Remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_changeCableLen(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change to new cable length @@ -231,13 +240,19 @@ def test_changeCableLen(self, dvs, testlog): self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 on interface + # Remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_MultipleLosslessPg(self, dvs, testlog): self.setup_db(dvs) - # configure lossless PG 3-4 on interface + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Add another lossless PG @@ -245,14 +260,14 @@ def test_MultipleLosslessPg(self, dvs, testlog): expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # change speed and check + # Change speed and check dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # change cable length and check + # Change cable length and check self.change_cable_length(self.cableLenTest1) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.cableLenTest1) @@ -261,7 +276,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # revert the speed and cable length and check + # Revert the speed and cable length and check self.change_cable_length(self.originalCableLen) dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -271,13 +286,19 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) - # remove lossless PG 3-4 and 6 on interface + # Remove lossless PG 3-4 and 6 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_headroomOverride(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + # Configure static profile self.config_db.update_entry('BUFFER_PROFILE', 'test', {'xon': '18432', @@ -352,9 +373,15 @@ def test_headroomOverride(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_mtuUpdate(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + test_mtu = '1500' default_mtu = '9100' expectedProfileMtu = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, mtu = test_mtu) @@ -380,9 +407,15 @@ def test_mtuUpdate(self, dvs, testlog): # clear configuration self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_nonDefaultAlpha(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + test_dynamic_th_1 = '1' expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1) test_dynamic_th_2 = '2' @@ -416,12 +449,17 @@ def test_nonDefaultAlpha(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic') + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + def test_sharedHeadroomPool(self, dvs, testlog): self.setup_db(dvs) + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + # configure lossless PG 3-4 on interface and start up the interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) - dvs.runcmd('config interface startup Ethernet0') expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -507,3 +545,37 @@ def test_sharedHeadroomPool(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') dvs.runcmd('config interface shutdown Ethernet0') + + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') + + def test_shutdownPort(self, dvs, testlog): + self.setup_db(dvs) + + # Startup interface + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on interface + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + + # Shutdown port and check whether all the PGs have been removed + dvs.runcmd("config interface shutdown Ethernet0") + self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") + self.app_db.wait_for_deleted_entry("BUFFER_PROFILE", expectedProfile) + + # Add another PG when port is administratively down + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'}) + + # Startup port and check whether all the PGs haved been added + dvs.runcmd("config interface startup Ethernet0") + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:" + expectedProfile + "]"}) + + # Remove lossless PG 3-4 on interface + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') + + # Shutdown interface + dvs.runcmd("config interface shutdown Ethernet0") From 15818ad5748eb836d8b75d7282cef9aade156f3f Mon Sep 17 00:00:00 2001 From: Vivek Reddy Date: Mon, 29 Mar 2021 07:26:54 -0400 Subject: [PATCH 54/54] [SFlowMgr] Sflow Crash on 200G ports handled (#1683) - What I did Added 200G entry into the speed-rate map. Also handled the case which programs empty string into APP-DB and thus leading to a failure of Orchagent. Default Sampling rate for 200G is set to 20000 - Why I did it Fix for Issue: Azure/sonic-buildimage#6793 - How I verified it run sflow community test under sonic-mgmt Co-authored-by: Vivek Reddy Karri --- cfgmgr/sflowmgr.cpp | 3 ++- cfgmgr/sflowmgr.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cfgmgr/sflowmgr.cpp b/cfgmgr/sflowmgr.cpp index aefcffe8e8..b7aeb8bbc1 100644 --- a/cfgmgr/sflowmgr.cpp +++ b/cfgmgr/sflowmgr.cpp @@ -13,6 +13,7 @@ using namespace swss; map sflowSpeedRateInitMap = { {SFLOW_SAMPLE_RATE_KEY_400G, SFLOW_SAMPLE_RATE_VALUE_400G}, + {SFLOW_SAMPLE_RATE_KEY_200G, SFLOW_SAMPLE_RATE_VALUE_200G}, {SFLOW_SAMPLE_RATE_KEY_100G, SFLOW_SAMPLE_RATE_VALUE_100G}, {SFLOW_SAMPLE_RATE_KEY_50G, SFLOW_SAMPLE_RATE_VALUE_50G}, {SFLOW_SAMPLE_RATE_KEY_40G, SFLOW_SAMPLE_RATE_VALUE_40G}, @@ -229,7 +230,7 @@ void SflowMgr::sflowCheckAndFillValues(string alias, vector &va { string speed = m_sflowPortConfMap[alias].speed; - if (speed != SFLOW_ERROR_SPEED_STR) + if (speed != SFLOW_ERROR_SPEED_STR && sflowSpeedRateInitMap.find(speed) != sflowSpeedRateInitMap.end()) { rate = sflowSpeedRateInitMap[speed]; } diff --git a/cfgmgr/sflowmgr.h b/cfgmgr/sflowmgr.h index 9f1d87cb8a..8e454efa7a 100644 --- a/cfgmgr/sflowmgr.h +++ b/cfgmgr/sflowmgr.h @@ -11,6 +11,7 @@ namespace swss { #define SFLOW_SAMPLE_RATE_KEY_400G "400000" +#define SFLOW_SAMPLE_RATE_KEY_200G "200000" #define SFLOW_SAMPLE_RATE_KEY_100G "100000" #define SFLOW_SAMPLE_RATE_KEY_50G "50000" #define SFLOW_SAMPLE_RATE_KEY_40G "40000" @@ -19,6 +20,7 @@ namespace swss { #define SFLOW_SAMPLE_RATE_KEY_1G "1000" #define SFLOW_SAMPLE_RATE_VALUE_400G "40000" +#define SFLOW_SAMPLE_RATE_VALUE_200G "20000" #define SFLOW_SAMPLE_RATE_VALUE_100G "10000" #define SFLOW_SAMPLE_RATE_VALUE_50G "5000" #define SFLOW_SAMPLE_RATE_VALUE_40G "4000"