diff --git a/neighsyncd/neighsync.h b/neighsyncd/neighsync.h index 9360a18713..66fd1c2645 100644 --- a/neighsyncd/neighsync.h +++ b/neighsyncd/neighsync.h @@ -11,10 +11,10 @@ /* * This is the timer value (in seconds) that the neighsyncd waits for restore_neighbors - * service to finish, should be longer than the restore_neighbors timeout value (60) + * service to finish, should be longer than the restore_neighbors timeout value (110) * This should not happen, if happens, system is in a unknown state, we should exit. */ -#define RESTORE_NEIGH_WAIT_TIME_OUT 70 +#define RESTORE_NEIGH_WAIT_TIME_OUT 120 namespace swss { diff --git a/neighsyncd/restore_neighbors.py b/neighsyncd/restore_neighbors.py index 387723dfe9..e0a0eea943 100755 --- a/neighsyncd/restore_neighbors.py +++ b/neighsyncd/restore_neighbors.py @@ -30,11 +30,12 @@ logger.setLevel(logging.WARNING) logger.addHandler(logging.NullHandler()) -# timeout the restore process in 1 min if not finished +# timeout the restore process in 110 seconds if not finished # This is mostly to wait for interfaces to be created and up after system warm-reboot # and this process is started by supervisord in swss docker. -# It would be good to keep that time below routing reconciliation time-out. -TIME_OUT = 60 +# There had been devices taking close to 70 seconds to complete restoration, setting +# default timeout to 110 seconds. +DEF_TIME_OUT = 110 # every 5 seconds to check interfaces states CHECK_INTERVAL = 5 @@ -189,13 +190,13 @@ def set_statedb_neigh_restore_done(): # Once all the entries are restored, this function is returned. # The interfaces' states were checked in a loop with an interval (CHECK_INTERVAL) # The function will timeout in case interfaces' states never meet the condition -# after some time (TIME_OUT). -def restore_update_kernel_neighbors(intf_neigh_map): +# after some time (DEF_TIME_OUT). +def restore_update_kernel_neighbors(intf_neigh_map, timeout=DEF_TIME_OUT): # create object for netlink calls to kernel ipclass = IPRoute() mtime = monotonic.time.time start_time = mtime() - while (mtime() - start_time) < TIME_OUT: + while (mtime() - start_time) < timeout: for intf, family_neigh_map in intf_neigh_map.items(): # only try to restore to kernel when link is up if is_intf_oper_state_up(intf): diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 1b42c978d5..4421caae8c 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -155,6 +155,7 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre return false; } + vrf_id = port.m_vr_id; addSubnetRoute(port, *ip_prefix); addIp2MeRoute(vrf_id, *ip_prefix); diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index ceba79a398..e479092b80 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -41,8 +41,8 @@ extern BufferOrch *gBufferOrch; #define DEFAULT_VLAN_ID 1 #define PORT_FLEX_STAT_COUNTER_POLL_MSECS "1000" #define QUEUE_FLEX_STAT_COUNTER_POLL_MSECS "10000" -#define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "1000" -#define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "1000" +#define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" +#define PG_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" static map fec_mode_map = @@ -252,9 +252,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) /* Get port hardware lane info */ for (i = 0; i < m_portCount; i++) { - sai_uint32_t lanes[4] = { 0,0,0,0 }; + sai_uint32_t lanes[8] = { 0,0,0,0,0,0,0,0 }; attr.id = SAI_PORT_ATTR_HW_LANE_LIST; - attr.value.u32list.count = 4; + attr.value.u32list.count = 8; attr.value.u32list.list = lanes; status = sai_port_api->get_port_attribute(port_list[i], 1, &attr); @@ -266,7 +266,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) set tmp_lane_set; for (j = 0; j < attr.value.u32list.count; j++) + { tmp_lane_set.insert(attr.value.u32list.list[j]); + } string tmp_lane_str = ""; for (auto s : tmp_lane_set) diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 06cde55f9a..417cce7ff5 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -15,6 +15,8 @@ #include "directory.h" #include "swssnet.h" #include "intfsorch.h" +#include "neighorch.h" +#include "crmorch.h" extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_route_api_t* sai_route_api; @@ -22,14 +24,16 @@ extern sai_object_id_t gSwitchId; extern Directory gDirectory; extern PortsOrch *gPortsOrch; extern IntfsOrch *gIntfsOrch; +extern NeighOrch *gNeighOrch; +extern CrmOrch *gCrmOrch; /* * VRF Modeling and VNetVrf class definitions */ std::vector vr_cntxt; -VNetVrfObject::VNetVrfObject(const std::string& vnet, string& tunnel, set& peer, - vector& attrs) : VNetObject(tunnel, peer) +VNetVrfObject::VNetVrfObject(const std::string& vnet, const VNetInfo& vnetInfo, + vector& attrs) : VNetObject(vnetInfo) { vnet_name_ = vnet; createObj(attrs); @@ -122,6 +126,113 @@ bool VNetVrfObject::updateObj(vector& attrs) return true; } +bool VNetVrfObject::hasRoute(IpPrefix& ipPrefix) +{ + if ((routes_.find(ipPrefix) != routes_.end()) || (tunnels_.find(ipPrefix) != tunnels_.end())) + { + return true; + } + + return false; +} + +bool VNetVrfObject::addRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp) +{ + if (hasRoute(ipPrefix)) + { + SWSS_LOG_INFO("VNET route '%s' exists", ipPrefix.to_string().c_str()); + return false; + } + + tunnels_[ipPrefix] = endp; + return true; +} + +bool VNetVrfObject::addRoute(IpPrefix& ipPrefix, nextHop& nh) +{ + if (hasRoute(ipPrefix)) + { + SWSS_LOG_INFO("VNET route '%s' exists", ipPrefix.to_string().c_str()); + return false; + } + + routes_[ipPrefix] = nh; + return true; +} + +bool VNetVrfObject::removeRoute(IpPrefix& ipPrefix) +{ + if (!hasRoute(ipPrefix)) + { + SWSS_LOG_INFO("VNET route '%s' does'nt exist", ipPrefix.to_string().c_str()); + return false; + } + /* + * Remove nexthop tunnel object before removing route + */ + + if (tunnels_.find(ipPrefix) != tunnels_.end()) + { + auto endp = tunnels_.at(ipPrefix); + removeTunnelNextHop(endp); + tunnels_.erase(ipPrefix); + } + else + { + routes_.erase(ipPrefix); + } + return true; +} + +size_t VNetVrfObject::getRouteCount() const +{ + return (routes_.size() + tunnels_.size()); +} + +bool VNetVrfObject::getRouteNextHop(IpPrefix& ipPrefix, nextHop& nh) +{ + if (!hasRoute(ipPrefix)) + { + SWSS_LOG_INFO("VNET route '%s' does'nt exist", ipPrefix.to_string().c_str()); + return false; + } + + nh = routes_.at(ipPrefix); + return true; +} + +sai_object_id_t VNetVrfObject::getTunnelNextHop(tunnelEndpoint& endp) +{ + sai_object_id_t nh_id = SAI_NULL_OBJECT_ID; + auto tun_name = getTunnelName(); + + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + + nh_id = vxlan_orch->createNextHopTunnel(tun_name, endp.ip, endp.mac, endp.vni); + if (nh_id == SAI_NULL_OBJECT_ID) + { + throw std::runtime_error("NH Tunnel create failed for " + vnet_name_ + " ip " + endp.ip.to_string()); + } + + return nh_id; +} + +bool VNetVrfObject::removeTunnelNextHop(tunnelEndpoint& endp) +{ + auto tun_name = getTunnelName(); + + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + + if (!vxlan_orch->removeNextHopTunnel(tun_name, endp.ip, endp.mac, endp.vni)) + { + SWSS_LOG_ERROR("VNET %s Tunnel NextHop remove failed for '%s'", + vnet_name_.c_str(), endp.ip.to_string().c_str()); + return false; + } + + return true; +} + VNetVrfObject::~VNetVrfObject() { set vr_ent = getVRids(); @@ -143,10 +254,10 @@ VNetVrfObject::~VNetVrfObject() */ template -std::unique_ptr VNetOrch::createObject(const string& vnet_name, string& tunnel, set& plist, +std::unique_ptr VNetOrch::createObject(const string& vnet_name, const VNetInfo& vnet_info, vector& attrs) { - std::unique_ptr vnet_obj(new T(vnet_name, tunnel, plist, attrs)); + std::unique_ptr vnet_obj(new T(vnet_name, vnet_info, attrs)); return vnet_obj; } @@ -165,19 +276,19 @@ VNetOrch::VNetOrch(DBConnector *db, const std::string& tableName, VNET_EXEC op) } } -bool VNetOrch::setIntf(const string& alias, const string vnet_name, const IpPrefix *prefix) +bool VNetOrch::setIntf(const string& alias, const string name, const IpPrefix *prefix) { SWSS_LOG_ENTER(); if (isVnetExecVrf()) { - if (!isVnetExists(vnet_name)) + if (!isVnetExists(name)) { - SWSS_LOG_WARN("VNET %s doesn't exist", vnet_name.c_str()); + SWSS_LOG_WARN("VNET %s doesn't exist", name.c_str()); return false; } - auto *vnet_obj = getTypePtr(vnet_name); + auto *vnet_obj = getTypePtr(name); sai_object_id_t vrf_id = vnet_obj->getVRidIngress(); return gIntfsOrch->setIntf(alias, vrf_id, prefix); @@ -220,7 +331,7 @@ bool VNetOrch::addOperation(const Request& request) } else { - SWSS_LOG_WARN("Logic error: Unknown attribute: %s", name.c_str()); + SWSS_LOG_INFO("Unknown attribute: %s", name.c_str()); continue; } } @@ -244,13 +355,14 @@ bool VNetOrch::addOperation(const Request& request) if (it == std::end(vnet_table_)) { - obj = createObject(vnet_name, tunnel, peer_list, attrs); + VNetInfo vnet_info = { tunnel, vni, peer_list }; + obj = createObject(vnet_name, vnet_info, attrs); create = true; } - VNetVrfObject *vrfObj = dynamic_cast(obj.get()); + VNetVrfObject *vrf_obj = dynamic_cast(obj.get()); if (!vxlan_orch->createVxlanTunnelMap(tunnel, TUNNEL_MAP_T_VIRTUAL_ROUTER, vni, - vrfObj->getEncapMapId(), vrfObj->getDecapMapId())) + vrf_obj->getEncapMapId(), vrf_obj->getDecapMapId())) { SWSS_LOG_ERROR("VNET '%s', tunnel '%s', map create failed", vnet_name.c_str(), tunnel.c_str()); @@ -302,9 +414,41 @@ bool VNetOrch::delOperation(const Request& request) return true; } + SWSS_LOG_INFO("VNET '%s' del request", vnet_name.c_str()); + + try + { + auto it = vnet_table_.find(vnet_name); + if (isVnetExecVrf()) + { + VxlanTunnelOrch* vxlan_orch = gDirectory.get(); + VNetVrfObject *vrf_obj = dynamic_cast(it->second.get()); + + if (vrf_obj->getRouteCount()) + { + SWSS_LOG_ERROR("VNET '%s': Routes are still present", vnet_name.c_str()); + return false; + } + + if (!vxlan_orch->removeVxlanTunnelMap(vrf_obj->getTunnelName(), vrf_obj->getVni())) + { + SWSS_LOG_ERROR("VNET '%s' map delete failed", vnet_name.c_str()); + return false; + } + } + else + { + // BRIDGE Handling + } + } + catch(std::runtime_error& _) + { + SWSS_LOG_ERROR("VNET del operation error for %s: error %s ", vnet_name.c_str(), _.what()); + return false; + } + vnet_table_.erase(vnet_name); - SWSS_LOG_INFO("VNET '%s' del request", vnet_name.c_str()); return true; } @@ -312,6 +456,32 @@ bool VNetOrch::delOperation(const Request& request) * Vnet Route Handling */ +static bool del_route(sai_object_id_t vr_id, sai_ip_prefix_t& ip_pfx) +{ + sai_route_entry_t route_entry; + route_entry.vr_id = vr_id; + route_entry.switch_id = gSwitchId; + route_entry.destination = ip_pfx; + + sai_status_t status = sai_route_api->remove_route_entry(&route_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("SAI Failed to remove route"); + return false; + } + + if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } + + return true; +} + static bool add_route(sai_object_id_t vr_id, sai_ip_prefix_t& ip_pfx, sai_object_id_t nh_id) { sai_route_entry_t route_entry; @@ -331,6 +501,15 @@ static bool add_route(sai_object_id_t vr_id, sai_ip_prefix_t& ip_pfx, sai_object return false; } + if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } + return true; } @@ -343,34 +522,9 @@ VNetRouteOrch::VNetRouteOrch(DBConnector *db, vector &tableNames, VNetOr handler_map_.insert(handler_pair(APP_VNET_RT_TUNNEL_TABLE_NAME, &VNetRouteOrch::handleTunnel)); } -sai_object_id_t VNetRouteOrch::getNextHop(const string& vnet, tunnelEndpoint& endp) -{ - auto it = nh_tunnels_.find(vnet); - if (it != nh_tunnels_.end()) - { - if (it->second.find(endp.ip) != it->second.end()) - { - return it->second.at(endp.ip); - } - } - - sai_object_id_t nh_id = SAI_NULL_OBJECT_ID; - auto tun_name = vnet_orch_->getTunnelName(vnet); - - VxlanTunnelOrch* vxlan_orch = gDirectory.get(); - - nh_id = vxlan_orch->createNextHopTunnel(tun_name, endp.ip, endp.mac, endp.vni); - if (nh_id == SAI_NULL_OBJECT_ID) - { - throw std::runtime_error("NH Tunnel create failed for " + vnet + " ip " + endp.ip.to_string()); - } - - nh_tunnels_[vnet].insert({endp.ip, nh_id}); - return nh_id; -} - template<> -bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, tunnelEndpoint& endp) +bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, + tunnelEndpoint& endp, string& op) { SWSS_LOG_ENTER(); @@ -400,23 +554,40 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP l_fn(peer); } + auto *vrf_obj = vnet_orch_->getTypePtr(vnet); sai_ip_prefix_t pfx; copy(pfx, ipPrefix); - sai_object_id_t nh_id = getNextHop(vnet, endp); + sai_object_id_t nh_id = (op == SET_COMMAND)?vrf_obj->getTunnelNextHop(endp):SAI_NULL_OBJECT_ID; for (auto vr_id : vr_set) { - if(!add_route(vr_id, pfx, nh_id)) + if (op == SET_COMMAND && !add_route(vr_id, pfx, nh_id)) { - SWSS_LOG_ERROR("Route add failed for %s", ipPrefix.to_string().c_str()); - break; + SWSS_LOG_ERROR("Route add failed for %s, vr_id '0x%lx", ipPrefix.to_string().c_str(), vr_id); + return false; + } + else if (op == DEL_COMMAND && !del_route(vr_id, pfx)) + { + SWSS_LOG_ERROR("Route del failed for %s, vr_id '0x%lx", ipPrefix.to_string().c_str(), vr_id); + return false; } } + + if (op == SET_COMMAND) + { + vrf_obj->addRoute(ipPrefix, endp); + } + else + { + vrf_obj->removeRoute(ipPrefix); + } + return true; } template<> -bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, string& ifname) +bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, + nextHop& nh, string& op) { SWSS_LOG_ENTER(); @@ -426,17 +597,43 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP return false; } - Port p; - if (!gPortsOrch->getPort(ifname, p) || (p.m_rif_id == SAI_NULL_OBJECT_ID)) + auto *vrf_obj = vnet_orch_->getTypePtr(vnet); + if (op == DEL_COMMAND && !vrf_obj->getRouteNextHop(ipPrefix, nh)) { - SWSS_LOG_WARN("Port/RIF %s doesn't exist", ifname.c_str()); + SWSS_LOG_WARN("VNET %s, Route %s get NH failed", vnet.c_str(), ipPrefix.to_string().c_str()); + return true; + } + + bool is_subnet = (!nh.ips.getSize())?true:false; + + Port port; + if (is_subnet && (!gPortsOrch->getPort(nh.ifname, port) || (port.m_rif_id == SAI_NULL_OBJECT_ID))) + { + SWSS_LOG_WARN("Port/RIF %s doesn't exist", nh.ifname.c_str()); return false; } set vr_set; auto& peer_list = vnet_orch_->getPeerList(vnet); - auto *vnet_obj = vnet_orch_->getTypePtr(vnet); - vr_set.insert(vnet_obj->getVRidEgress()); + auto vr_id = vrf_obj->getVRidIngress(); + + /* + * If RIF doesn't belong to this VRF, and if it is a replicated subnet + * route for the peering VRF, Only install in ingress VRF. + */ + + if (!is_subnet) + { + vr_set = vrf_obj->getVRids(); + } + else if (vr_id == port.m_vr_id) + { + vr_set.insert(vrf_obj->getVRidEgress()); + } + else + { + vr_set.insert(vr_id); + } auto l_fn = [&] (const string& vnet) { auto *vnet_obj = vnet_orch_->getTypePtr(vnet); @@ -456,23 +653,64 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP sai_ip_prefix_t pfx; copy(pfx, ipPrefix); + sai_object_id_t nh_id=SAI_NULL_OBJECT_ID; + + if (is_subnet) + { + nh_id = port.m_rif_id; + } + else if (nh.ips.getSize() == 1) + { + IpAddress ip_address(nh.ips.to_string()); + if (gNeighOrch->hasNextHop(ip_address)) + { + nh_id = gNeighOrch->getNextHopId(ip_address); + } + else + { + SWSS_LOG_INFO("Failed to get next hop %s for %s", + ip_address.to_string().c_str(), ipPrefix.to_string().c_str()); + return false; + } + } + else + { + // FIXME - Handle ECMP routes + SWSS_LOG_WARN("VNET ECMP NHs not implemented for '%s'", ipPrefix.to_string().c_str()); + return true; + } for (auto vr_id : vr_set) { - if(!add_route(vr_id, pfx, p.m_rif_id )) + if (op == SET_COMMAND && !add_route(vr_id, pfx, nh_id)) { SWSS_LOG_ERROR("Route add failed for %s", ipPrefix.to_string().c_str()); break; } + else if (op == DEL_COMMAND && !del_route(vr_id, pfx)) + { + SWSS_LOG_ERROR("Route del failed for %s", ipPrefix.to_string().c_str()); + break; + } + } + + if (op == SET_COMMAND) + { + vrf_obj->addRoute(ipPrefix, nh); + } + else + { + vrf_obj->removeRoute(ipPrefix); } return true; } -void VNetRouteOrch::handleRoutes(const Request& request) +bool VNetRouteOrch::handleRoutes(const Request& request) { SWSS_LOG_ENTER(); + IpAddresses ip_addresses; string ifname = ""; for (const auto& name: request.getAttrFieldNames()) @@ -481,28 +719,35 @@ void VNetRouteOrch::handleRoutes(const Request& request) { ifname = request.getAttrString(name); } + else if (name == "nexthop") + { + auto ipstr = request.getAttrString(name); + ip_addresses = IpAddresses(ipstr); + } else { - SWSS_LOG_WARN("Logic error: Unknown attribute: %s", name.c_str()); - return; + SWSS_LOG_INFO("Unknown attribute: %s", name.c_str()); + continue; } } const std::string& vnet_name = request.getKeyString(0); auto ip_pfx = request.getKeyIpPrefix(1); + auto op = request.getOperation(); + nextHop nh = { ip_addresses, ifname }; - SWSS_LOG_INFO("VNET-RT '%s' add for ip %s", vnet_name.c_str(), ip_pfx.to_string().c_str()); + SWSS_LOG_INFO("VNET-RT '%s' op '%s' for ip %s", vnet_name.c_str(), + op.c_str(), ip_pfx.to_string().c_str()); if (vnet_orch_->isVnetExecVrf()) { - if (!doRouteTask(vnet_name, ip_pfx, ifname)) - { - throw std::runtime_error("Route add failed"); - } + return doRouteTask(vnet_name, ip_pfx, nh, op); } + + return true; } -void VNetRouteOrch::handleTunnel(const Request& request) +bool VNetRouteOrch::handleTunnel(const Request& request) { SWSS_LOG_ENTER(); @@ -526,24 +771,26 @@ void VNetRouteOrch::handleTunnel(const Request& request) } else { - SWSS_LOG_WARN("Logic error: Unknown attribute: %s", name.c_str()); - return; + SWSS_LOG_INFO("Unknown attribute: %s", name.c_str()); + continue; } } const std::string& vnet_name = request.getKeyString(0); auto ip_pfx = request.getKeyIpPrefix(1); + auto op = request.getOperation(); - SWSS_LOG_INFO("VNET-RT '%s' add for endpoint %s", vnet_name.c_str(), ip_pfx.to_string().c_str()); + SWSS_LOG_INFO("VNET-RT '%s' op '%s' for pfx %s", vnet_name.c_str(), + op.c_str(), ip_pfx.to_string().c_str()); tunnelEndpoint endp = { ip, mac, vni }; + if (vnet_orch_->isVnetExecVrf()) { - if (!doRouteTask(vnet_name, ip_pfx, endp)) - { - throw std::runtime_error("Route add failed"); - } + return doRouteTask(vnet_name, ip_pfx, endp, op); } + + return true; } bool VNetRouteOrch::addOperation(const Request& request) @@ -559,12 +806,12 @@ bool VNetRouteOrch::addOperation(const Request& request) return true; } - (this->*(handler_map_[tn]))(request); + return ((this->*(handler_map_[tn]))(request)); } catch(std::runtime_error& _) { SWSS_LOG_ERROR("VNET add operation error %s ", _.what()); - return false; + return true; } return true; @@ -574,7 +821,22 @@ bool VNetRouteOrch::delOperation(const Request& request) { SWSS_LOG_ENTER(); - SWSS_LOG_ERROR("DEL operation is not implemented"); + try + { + auto& tn = request.getTableName(); + if (handler_map_.find(tn) == handler_map_.end()) + { + SWSS_LOG_ERROR(" %s handler is not initialized", tn.c_str()); + return true; + } + + return ((this->*(handler_map_[tn]))(request)); + } + catch(std::runtime_error& _) + { + SWSS_LOG_ERROR("VNET del operation error %s ", _.what()); + return true; + } return true; } diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 367616529c..59e8317f45 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -7,6 +7,7 @@ #include #include "request_parser.h" +#include "ipaddresses.h" extern sai_object_id_t gVirtualRouterId; @@ -17,6 +18,7 @@ const request_description_t vnet_request_description = { { "vxlan_tunnel", REQ_T_STRING }, { "vni", REQ_T_UINT }, { "peer_list", REQ_T_SET }, + { "guid", REQ_T_STRING }, }, { "vxlan_tunnel", "vni" } // mandatory attributes }; @@ -35,6 +37,13 @@ enum class VR_TYPE VR_INVALID }; +struct VNetInfo +{ + string tunnel; + uint32_t vni; + set peers; +}; + typedef map vrid_list_t; extern std::vector vr_cntxt; @@ -47,7 +56,11 @@ class VNetRequest : public Request class VNetObject { public: - VNetObject(string& tunName, set& peer) : tunnel_(tunName), peer_list_(peer) { } + VNetObject(const VNetInfo& vnetInfo) : + tunnel_(vnetInfo.tunnel), + peer_list_(vnetInfo.peers), + vni_(vnetInfo.vni) + { } virtual bool updateObj(vector&) = 0; @@ -66,17 +79,39 @@ class VNetObject return tunnel_; } + uint32_t getVni() const + { + return vni_; + } + virtual ~VNetObject() {}; private: set peer_list_ = {}; string tunnel_; + uint32_t vni_; +}; + +struct tunnelEndpoint +{ + IpAddress ip; + MacAddress mac; + uint32_t vni; +}; + +struct nextHop +{ + IpAddresses ips; + string ifname; }; +typedef std::map TunnelRoutes; +typedef std::map RouteMap; + class VNetVrfObject : public VNetObject { public: - VNetVrfObject(const string& vnet, string& tunnel, set& peer, vector& attrs); + VNetVrfObject(const string& vnet, const VNetInfo& vnetInfo, vector& attrs); sai_object_id_t getVRidIngress() const; @@ -103,11 +138,25 @@ class VNetVrfObject : public VNetObject bool updateObj(vector&); + bool addRoute(IpPrefix& ipPrefix, tunnelEndpoint& endp); + bool addRoute(IpPrefix& ipPrefix, nextHop& nh); + bool removeRoute(IpPrefix& ipPrefix); + + size_t getRouteCount() const; + bool getRouteNextHop(IpPrefix& ipPrefix, nextHop& nh); + bool hasRoute(IpPrefix& ipPrefix); + + sai_object_id_t getTunnelNextHop(tunnelEndpoint& endp); + bool removeTunnelNextHop(tunnelEndpoint& endp); + ~VNetVrfObject(); private: string vnet_name_; vrid_list_t vr_ids_; + + TunnelRoutes tunnels_; + RouteMap routes_; }; typedef std::unique_ptr VNetObject_T; @@ -117,7 +166,8 @@ class VNetOrch : public Orch2 { public: VNetOrch(DBConnector *db, const std::string&, VNET_EXEC op = VNET_EXEC::VNET_EXEC_VRF); - bool setIntf(const string& alias, const string vnet_name, const IpPrefix *prefix = nullptr); + + bool setIntf(const string& alias, const string name, const IpPrefix *prefix = nullptr); bool isVnetExists(const std::string& name) const { @@ -155,7 +205,7 @@ class VNetOrch : public Orch2 virtual bool delOperation(const Request& request); template - std::unique_ptr createObject(const string&, string&, set&, vector&); + std::unique_ptr createObject(const string&, const VNetInfo&, vector&); VNetTable vnet_table_; VNetRequest request_; @@ -168,6 +218,7 @@ const request_description_t vnet_route_description = { { { "endpoint", REQ_T_IP }, { "ifname", REQ_T_STRING }, + { "nexthop", REQ_T_STRING }, { "vni", REQ_T_UINT }, { "mac_address", REQ_T_MAC_ADDRESS }, }, @@ -180,43 +231,30 @@ class VNetRouteRequest : public Request VNetRouteRequest() : Request(vnet_route_description, ':') { } }; -typedef map NextHopMap; -typedef map NextHopTunnels; - -struct tunnelEndpoint -{ - IpAddress ip; - MacAddress mac; - uint32_t vni; -}; - class VNetRouteOrch : public Orch2 { public: VNetRouteOrch(DBConnector *db, vector &tableNames, VNetOrch *); - typedef pair handler_pair; - typedef map handler_map; + typedef pair handler_pair; + typedef map handler_map; private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); - void handleRoutes(const Request&); - void handleTunnel(const Request&); + bool handleRoutes(const Request&); + bool handleTunnel(const Request&); template - bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, tunnelEndpoint& endp); + bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, tunnelEndpoint& endp, string& op); template - bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, string& ifname); - - sai_object_id_t getNextHop(const string& vnet, tunnelEndpoint& endp); + bool doRouteTask(const string& vnet, IpPrefix& ipPrefix, nextHop& nh, string& op); VNetOrch *vnet_orch_; VNetRouteRequest request_; handler_map handler_map_; - NextHopTunnels nh_tunnels_; }; #endif // __VNETORCH_H diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 997920db6f..66ae51fad1 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -155,6 +155,21 @@ static sai_object_id_t create_tunnel_map_entry( return tunnel_map_entry_id; } +void remove_tunnel_map_entry(sai_object_id_t obj_id) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + + if (obj_id != SAI_NULL_OBJECT_ID) + { + status = sai_tunnel_api->remove_tunnel_map_entry(obj_id); + } + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't delete a tunnel map entry object"); + } +} + static sai_status_t create_nexthop_tunnel( sai_ip_address_t host_ip, sai_uint32_t vni, // optional vni @@ -371,15 +386,111 @@ sai_object_id_t VxlanTunnel::addDecapMapperEntry(sai_object_id_t obj, uint32_t v return create_tunnel_map_entry(map_t, decap_id, vni, 0, obj); } +void VxlanTunnel::insertMapperEntry(sai_object_id_t encap, sai_object_id_t decap, uint32_t vni) +{ + tunnel_map_entries_[vni] = std::pair(encap, decap); +} + +std::pair VxlanTunnel::getMapperEntry(uint32_t vni) +{ + if (tunnel_map_entries_.find(vni) != tunnel_map_entries_.end()) + { + return tunnel_map_entries_[vni]; + } + + return std::make_pair(SAI_NULL_OBJECT_ID, SAI_NULL_OBJECT_ID); +} + +void VxlanTunnel::updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nh_id) +{ + auto key = nh_key_t(ipAddr, macAddress, vni); + + auto it = nh_tunnels_.find(key); + if (it == nh_tunnels_.end()) + { + nh_tunnels_[key] = {nh_id, 1}; + return; + } +} + +sai_object_id_t VxlanTunnel::getNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) const +{ + auto key = nh_key_t(ipAddr, macAddress, vni); + + auto it = nh_tunnels_.find(key); + if (it == nh_tunnels_.end()) + { + return SAI_NULL_OBJECT_ID; + } + + return nh_tunnels_.at(key).nh_id; +} + +void VxlanTunnel::incNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +{ + auto key = nh_key_t(ipAddr, macAddress, vni); + nh_tunnels_[key].ref_count ++; +} + +void VxlanTunnel::decNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +{ + auto key = nh_key_t(ipAddr, macAddress, vni); + nh_tunnels_[key].ref_count --; +} + +bool VxlanTunnel::removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +{ + auto key = nh_key_t(ipAddr, macAddress, vni); + + auto it = nh_tunnels_.find(key); + if (it == nh_tunnels_.end()) + { + SWSS_LOG_INFO("NH tunnel for '%s' doesn't exist", ipAddr.to_string().c_str()); + return false; + } + + SWSS_LOG_INFO("NH tunnel for ip '%s' ref_count '%d'", ipAddr.to_string().c_str(), nh_tunnels_[key].ref_count); + + //Decrement ref count if already exists + nh_tunnels_[key].ref_count --; + + if (!nh_tunnels_[key].ref_count) + { + if (sai_next_hop_api->remove_next_hop(nh_tunnels_[key].nh_id) != SAI_STATUS_SUCCESS) + { + string err_msg = "NH tunnel delete failed for " + ipAddr.to_string(); + throw std::runtime_error(err_msg); + } + + nh_tunnels_.erase(key); + } + + SWSS_LOG_INFO("NH tunnel for ip '%s', mac '%s' updated/deleted", + ipAddr.to_string().c_str(), macAddress.to_string().c_str()); + + return true; +} + sai_object_id_t VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) { + SWSS_LOG_ENTER(); + if(!isTunnelExists(tunnelName)) { SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); return SAI_NULL_OBJECT_ID; } + auto tunnel_obj = getVxlanTunnel(tunnelName); + sai_object_id_t nh_id, tunnel_id = tunnel_obj->getTunnelId(); + + if ((nh_id = tunnel_obj->getNextHop(ipAddr, macAddress, vni)) != SAI_NULL_OBJECT_ID) + { + tunnel_obj->incNextHopRefCount(ipAddr, macAddress, vni); + return nh_id; + } + sai_ip_address_t host_ip; swss::copy(host_ip, ipAddr); @@ -390,20 +501,36 @@ VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAd macptr = &mac; } - auto tunnel_obj = getVxlanTunnel(tunnelName); - - sai_object_id_t nh_id, tunnel_id = tunnel_obj->getTunnelId(); - if (create_nexthop_tunnel(host_ip, vni, macptr, tunnel_id, &nh_id) != SAI_STATUS_SUCCESS) { string err_msg = "NH tunnel create failed for " + ipAddr.to_string() + " " + to_string(vni); throw std::runtime_error(err_msg); } + //Store the nh tunnel id + tunnel_obj->updateNextHop(ipAddr, macAddress, vni, nh_id); + SWSS_LOG_INFO("NH vxlan tunnel was created for %s, id 0x%lx", tunnelName.c_str(), nh_id); return nh_id; } +bool +VxlanTunnelOrch::removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) +{ + SWSS_LOG_ENTER(); + + if(!isTunnelExists(tunnelName)) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); + return true; + } + + auto tunnel_obj = getVxlanTunnel(tunnelName); + + //Delete request for the nh tunnel id + return tunnel_obj->removeNextHop(ipAddr, macAddress, vni); +} + bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t map, uint32_t vni, sai_object_id_t encap, sai_object_id_t decap) { @@ -437,6 +564,8 @@ bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t auto encap_id = tunnel_obj->addEncapMapperEntry(encap, vni); auto decap_id = tunnel_obj->addDecapMapperEntry(decap, vni); + tunnel_obj->insertMapperEntry(encap_id, decap_id, vni); + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", encap_id, decap_id); } catch(const std::runtime_error& error) @@ -450,6 +579,48 @@ bool VxlanTunnelOrch::createVxlanTunnelMap(string tunnelName, tunnel_map_type_t return true; } +bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) +{ + SWSS_LOG_ENTER(); + + if(!isTunnelExists(tunnelName)) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' does not exists", tunnelName.c_str()); + return false; + } + + auto tunnel_obj = getVxlanTunnel(tunnelName); + + if (!tunnel_obj->isActive()) + { + SWSS_LOG_ERROR("Vxlan tunnel '%s' is not Active", tunnelName.c_str()); + return false; + } + + try + { + /* + * Delete encap and decap mapper + */ + + std::pair mapper = tunnel_obj->getMapperEntry(vni); + + remove_tunnel_map_entry(mapper.first); + remove_tunnel_map_entry(mapper.second); + + SWSS_LOG_DEBUG("Vxlan tunnel encap entry '%lx' decap entry '0x%lx'", mapper.first, mapper.second); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error removing tunnel map entry. Tunnel: %s. Error: %s", + tunnelName.c_str(), error.what()); + return false; + } + + SWSS_LOG_NOTICE("Vxlan map entry deleted for tunnel '%s' with vni '%d'", tunnelName.c_str(), vni); + return true; +} + bool VxlanTunnelOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index f5e3774e7d..e4d4bcd4ef 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include "request_parser.h" @@ -26,6 +27,50 @@ struct tunnel_ids_t sai_object_id_t tunnel_term_id; }; +struct nh_key_t +{ + IpAddress ip_addr; + MacAddress mac_address; + uint32_t vni=0; + + nh_key_t() = default; + + nh_key_t(IpAddress ipAddr, MacAddress macAddress=MacAddress(), uint32_t vnId=0) + { + ip_addr = ipAddr; + mac_address = macAddress; + vni = vnId; + }; + + bool operator== (const nh_key_t& rhs) const + { + if (!(ip_addr == rhs.ip_addr) || mac_address != rhs.mac_address || vni != rhs.vni) + { + return false; + } + return true; + } +}; + +struct nh_key_hash +{ + size_t operator() (const nh_key_t& key) const + { + stringstream ss; + ss << key.ip_addr.to_string() << key.mac_address.to_string() << std::to_string(key.vni); + return std::hash() (ss.str()); + } +}; + +struct nh_tunnel_t +{ + sai_object_id_t nh_id; + int ref_count; +}; + +typedef std::map> TunnelMapEntries; +typedef std::unordered_map TunnelNHs; + class VxlanTunnel { public: @@ -41,6 +86,9 @@ class VxlanTunnel sai_object_id_t addEncapMapperEntry(sai_object_id_t obj, uint32_t vni); sai_object_id_t addDecapMapperEntry(sai_object_id_t obj, uint32_t vni); + void insertMapperEntry(sai_object_id_t encap, sai_object_id_t decap, uint32_t vni); + std::pair getMapperEntry(uint32_t vni); + sai_object_id_t getTunnelId() const { return ids_.tunnel_id; @@ -56,6 +104,13 @@ class VxlanTunnel return ids_.tunnel_encap_id; } + void updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nhId); + bool removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); + sai_object_id_t getNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) const; + + void incNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); + void decNextHopRefCount(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); + private: string tunnel_name_; bool active_ = false; @@ -63,6 +118,9 @@ class VxlanTunnel tunnel_ids_t ids_; std::pair tunnel_map_ = { MAP_T::MAP_TO_INVALID, MAP_T::MAP_TO_INVALID }; + TunnelMapEntries tunnel_map_entries_; + TunnelNHs nh_tunnels_; + IpAddress src_ip_; IpAddress dst_ip_ = 0x0; }; @@ -110,9 +168,14 @@ class VxlanTunnelOrch : public Orch2 bool createVxlanTunnelMap(string tunnelName, tunnel_map_type_t mapType, uint32_t vni, sai_object_id_t encap, sai_object_id_t decap); + bool removeVxlanTunnelMap(string tunnelName, uint32_t vni); + sai_object_id_t createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); + bool + removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); diff --git a/tests/test_port_an_warm.py b/tests/test_port_an_warm.py index 4a768d03a8..50ffbbf819 100644 --- a/tests/test_port_an_warm.py +++ b/tests/test_port_an_warm.py @@ -12,9 +12,9 @@ def test_PortAutoNeg_warm(dvs, testlog): ctbl = swsscommon.Table(cdb, "PORT") stbl = swsscommon.Table(sdb, "PORT_TABLE") - # set autoneg = false and speed = 1000 + # set autoneg = true and speed = 1000 fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) - tbl.set("Ethernet0", fvs) + ctbl.set("Ethernet0", fvs) time.sleep(1) @@ -35,7 +35,7 @@ def test_PortAutoNeg_warm(dvs, testlog): # set speed = 100 fvs = swsscommon.FieldValuePairs([("speed", "100")]) - tbl.set("Ethernet0", fvs) + ctbl.set("Ethernet0", fvs) time.sleep(1) diff --git a/tests/test_speed.py b/tests/test_speed.py index f698aaec5b..765383b60a 100644 --- a/tests/test_speed.py +++ b/tests/test_speed.py @@ -16,7 +16,8 @@ class TestSpeedSet(object): num_ports = 32 def test_SpeedAndBufferSet(self, dvs, testlog): - speed_list = ['50000', '25000', '40000', '10000', '100000'] + configured_speed_list = ['40000'] + speed_list = ['10000', '25000', '40000', '50000', '100000'] cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -28,11 +29,11 @@ def test_SpeedAndBufferSet(self, dvs, testlog): buffer_profiles = cfg_buffer_profile_table.getKeys() expected_buffer_profiles_num = len(buffer_profiles) - # buffers.json used for the test defines 4 static profiles: - # "ingress_lossless_profile" + # buffers_config.j2 used for the test defines 3 static profiles and 1 dynamic profiles: # "ingress_lossy_profile" # "egress_lossless_profile" # "egress_lossy_profile" + # "pg_lossless_40000_300m_profile" # check if they get the DB assert expected_buffer_profiles_num == 4 # and if they were successfully created on ASIC @@ -61,7 +62,10 @@ def test_SpeedAndBufferSet(self, dvs, testlog): assert num_set == self.num_ports # check number of created profiles - expected_buffer_profiles_num += 1 # new speed should add new PG profile + if speed not in configured_speed_list: + expected_buffer_profiles_num += 1 # new speed should add new PG profile + configured_speed_list.append(speed) + current_buffer_profiles = cfg_buffer_profile_table.getKeys() assert len(current_buffer_profiles) == expected_buffer_profiles_num # make sure the same number of profiles are created on ASIC diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py index 4b6b849c3c..7815b40296 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -967,8 +967,7 @@ def enable_warmrestart(dvs, db, app_name): # ################################################################################ -# TODO: Please fix this test case. Here temporarily skip to unblock other pull requests -@pytest.mark.skip(reason="Suspected unstable test code") + def test_routing_WarmRestart(dvs, testlog): appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) @@ -976,7 +975,7 @@ def test_routing_WarmRestart(dvs, testlog): state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) # Restart-timer to utilize during the following testcases - restart_timer = 10 + restart_timer = 15 ############################################################################# diff --git a/tests/test_watermark.py b/tests/test_watermark.py index a3dc8ecf0f..80602929c9 100644 --- a/tests/test_watermark.py +++ b/tests/test_watermark.py @@ -22,6 +22,7 @@ class TestWatermark(object): DEFAULT_TELEMETRY_INTERVAL = 120 NEW_INTERVAL = 5 + DEFAULT_POLL_INTERVAL = 10 def enable_unittests(self, dvs, status): db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -57,7 +58,7 @@ def populate_asic_all(self, dvs, val): self.populate_asic(dvs, "SAI_OBJECT_TYPE_QUEUE", SaiWmStats.queue_shared, val) self.populate_asic(dvs, "SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", SaiWmStats.pg_shared, val) self.populate_asic(dvs, "SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", SaiWmStats.pg_headroom, val) - time.sleep(1) + time.sleep(self.DEFAULT_POLL_INTERVAL) def verify_value(self, dvs, obj_ids, table_name, watermark_name, expected_value):