From d9e23ade934d25be5c5dc983995388b4aeec7066 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Wed, 9 Jun 2021 05:38:58 +0000 Subject: [PATCH 01/28] Vxlan evpn p2mp changes for Layer2 functionality --- orchagent/fdborch.cpp | 43 +- orchagent/fdborch.h | 2 +- orchagent/main.cpp | 28 + orchagent/orchdaemon.cpp | 23 +- orchagent/port.h | 6 + orchagent/portsorch.cpp | 312 +++++- orchagent/portsorch.h | 12 +- orchagent/routeorch.cpp | 8 + orchagent/saihelper.cpp | 3 + orchagent/vxlanorch.cpp | 200 +++- orchagent/vxlanorch.h | 35 +- tests/evpn_tunnel.py | 1143 ++++++++++++++++++++ tests/mock_tests/mock_orchagent_main.cpp | 1 + tests/mock_tests/mock_orchagent_main.h | 1 + tests/test_evpn_fdb.py | 156 +-- tests/test_evpn_fdb_p2mp.py | 392 +++++++ tests/test_evpn_l3_vxlan.py | 1207 ++-------------------- tests/test_evpn_l3_vxlan_p2mp.py | 596 +++++++++++ tests/test_evpn_p2p_tunnel.py | 690 +++++++++++++ tests/test_evpn_tunnel.py | 613 +---------- tests/test_evpn_tunnel_p2mp.py | 119 +++ 21 files changed, 3743 insertions(+), 1847 deletions(-) create mode 100644 tests/evpn_tunnel.py create mode 100644 tests/test_evpn_fdb_p2mp.py create mode 100644 tests/test_evpn_l3_vxlan_p2mp.py create mode 100644 tests/test_evpn_p2p_tunnel.py create mode 100644 tests/test_evpn_tunnel_p2mp.py diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index af46d958ec..df9612e0b9 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -630,17 +630,33 @@ void FdbOrch::doTask(Consumer& consumer) /* FDB type is either dynamic or static */ assert(type == "dynamic" || type == "static"); + bool check_vlan_member = true; if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { VxlanTunnelOrch* tunnel_orch = gDirectory.get(); - if(!remote_ip.length()) + if (tunnel_orch->dipTunnelsUsed()) { - it = consumer.m_toSync.erase(it); - continue; + if(!remote_ip.length()) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(remote_ip); + } + else + { + EvpnNvoOrch* evpn_nvo_orch = gDirectory.get(); + VxlanTunnel* sip_tunnel = evpn_nvo_orch->getEVPNVtep(); + check_vlan_member = false; + if (sip_tunnel == NULL) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(sip_tunnel->getSrcIP().to_string(), true); } - port = tunnel_orch->getTunnelPortName(remote_ip); } @@ -651,7 +667,7 @@ void FdbOrch::doTask(Consumer& consumer) fdbData.remote_ip = remote_ip; fdbData.esi = esi; fdbData.vni = vni; - if (addFdbEntry(entry, port, fdbData)) + if (addFdbEntry(entry, port, fdbData, check_vlan_member)) it = consumer.m_toSync.erase(it); else it++; @@ -959,15 +975,15 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) } bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, - FdbData fdbData) + FdbData fdbData, bool check_vlan_member) { Port vlan; Port port; SWSS_LOG_ENTER(); - SWSS_LOG_INFO("mac=%s bv_id=0x%" PRIx64 " port_name=%s type=%s origin=%d", + SWSS_LOG_INFO("mac=%s bv_id=0x%" PRIx64 " port_name=%s type=%s origin=%d remote_ip=%s", entry.mac.to_string().c_str(), entry.bv_id, port_name.c_str(), - fdbData.type.c_str(), fdbData.origin); + fdbData.type.c_str(), fdbData.origin, fdbData.remote_ip.c_str()); if (!m_portsOrch->getPort(entry.bv_id, vlan)) { @@ -985,7 +1001,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, } /* Retry until port is member of vlan*/ - if (vlan.m_members.find(port_name) == vlan.m_members.end()) + if (check_vlan_member && vlan.m_members.find(port_name) == vlan.m_members.end()) { SWSS_LOG_INFO("Saving a fdb entry until port %s becomes vlan %s member", port_name.c_str(), vlan.m_alias.c_str()); saved_fdb_entries[port_name].push_back({entry.mac, @@ -1001,6 +1017,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, Port oldPort; string oldType; + string oldRemoteIp; FdbOrigin oldOrigin = FDB_ORIGIN_INVALID ; bool macUpdate = false; auto it = m_entries.find(entry); @@ -1009,6 +1026,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, /* get existing port and type */ oldType = it->second.type; oldOrigin = it->second.origin; + oldRemoteIp = it->second.remote_ip; if (!m_portsOrch->getPortByBridgePortId(it->second.bridge_port_id, oldPort)) { @@ -1016,12 +1034,13 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, return false; } - if ((oldOrigin == fdbData.origin) && (oldType == fdbData.type) && (port.m_bridge_port_id == it->second.bridge_port_id)) + if ((oldOrigin == fdbData.origin) && (oldType == fdbData.type) && (port.m_bridge_port_id == it->second.bridge_port_id) + && (oldRemoteIp == fdbData.remote_ip)) { /* Duplicate Mac */ - SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d is duplicate", entry.mac.to_string().c_str(), + SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d remote_ip=%s is duplicate", entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), - fdbData.type.c_str(), fdbData.origin); + fdbData.type.c_str(), fdbData.origin, fdbData.remote_ip.c_str()); return true; } else if (fdbData.origin != oldOrigin) diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 201493f574..d67de514c9 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -114,7 +114,7 @@ class FdbOrch: public Orch, public Subject, public Observer void updateVlanMember(const VlanMemberUpdate&); void updatePortOperState(const PortOperStateUpdate&); - bool addFdbEntry(const FdbEntry&, const string&, FdbData fdbData); + bool addFdbEntry(const FdbEntry&, const string&, FdbData fdbData, bool check_vlan_member=true); void deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, FdbOrigin origin, const string portName=""); bool storeFdbEntryState(const FdbUpdate& update); diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 018af5999b..bb8c915fc4 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -46,6 +46,7 @@ sai_object_id_t gUnderlayIfId; sai_object_id_t gSwitchId = SAI_NULL_OBJECT_ID; MacAddress gMacAddress; MacAddress gVxlanMacAddress; +bool gP2PTunnelSupported; extern size_t gMaxBulkSize; @@ -643,6 +644,33 @@ int main(int argc, char **argv) orchDaemon = make_shared(&appl_db, &config_db, &state_db, chassis_app_db.get()); } + uint32_t max_tunnel_modes = 2; + vector tunnel_peer_modes(max_tunnel_modes, 0); + sai_s32_list_t values; + values.count = max_tunnel_modes; + values.list = tunnel_peer_modes.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_TUNNEL, + SAI_TUNNEL_ATTR_PEER_MODE, &values); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Unable to get supported tunnel peer modes. Defaulting to P2P"); + gP2PTunnelSupported = true; + } + else + { + gP2PTunnelSupported = false; + for (uint32_t idx = 0; idx < values.count; idx++) + { + if (values.list[idx] == SAI_TUNNEL_PEER_MODE_P2P) + { + gP2PTunnelSupported = true; + break; + } + } + } + auto orchDaemon = make_shared(&appl_db, &config_db, &state_db, chassis_app_db.get()); + if (!orchDaemon->init()) { SWSS_LOG_ERROR("Failed to initialize orchestration daemon"); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index f8f00cd225..a52c4ed88b 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -20,6 +20,7 @@ using namespace swss; extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern bool gSaiRedisLogRotate; +extern bool gP2PTunnelSupported; extern void syncd_apply_view(); /* @@ -163,19 +164,18 @@ bool OrchDaemon::init() CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); - VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); - gDirectory.set(vxlan_tunnel_orch); VxlanTunnelMapOrch *vxlan_tunnel_map_orch = new VxlanTunnelMapOrch(m_applDb, APP_VXLAN_TUNNEL_MAP_TABLE_NAME); gDirectory.set(vxlan_tunnel_map_orch); VxlanVrfMapOrch *vxlan_vrf_orch = new VxlanVrfMapOrch(m_applDb, APP_VXLAN_VRF_TABLE_NAME); gDirectory.set(vxlan_vrf_orch); - EvpnRemoteVniOrch* evpn_remote_vni_orch = new EvpnRemoteVniOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); - gDirectory.set(evpn_remote_vni_orch); EvpnNvoOrch* evpn_nvo_orch = new EvpnNvoOrch(m_applDb, APP_VXLAN_EVPN_NVO_TABLE_NAME); gDirectory.set(evpn_nvo_orch); + VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, + APP_VXLAN_TUNNEL_TABLE_NAME, gP2PTunnelSupported); + gDirectory.set(vxlan_tunnel_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -334,7 +334,20 @@ bool OrchDaemon::init() m_orchList.push_back(vxlan_tunnel_orch); m_orchList.push_back(evpn_nvo_orch); m_orchList.push_back(vxlan_tunnel_map_orch); - m_orchList.push_back(evpn_remote_vni_orch); + + if (gP2PTunnelSupported) + { + EvpnRemoteVnip2pOrch* evpn_remote_vni_orch = new EvpnRemoteVnip2pOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); + gDirectory.set(evpn_remote_vni_orch); + m_orchList.push_back(evpn_remote_vni_orch); + } + else + { + EvpnRemoteVnip2mpOrch* evpn_remote_vni_orch = new EvpnRemoteVnip2mpOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); + gDirectory.set(evpn_remote_vni_orch); + m_orchList.push_back(evpn_remote_vni_orch); + } + m_orchList.push_back(vxlan_vrf_orch); m_orchList.push_back(cfg_vnet_rt_orch); m_orchList.push_back(vnet_orch); diff --git a/orchagent/port.h b/orchagent/port.h index 57c9f6e2d9..31c3392fcd 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -38,11 +38,17 @@ struct VlanMemberEntry typedef std::map vlan_members_t; +typedef std::map endpoint_ip_l2mc_group_member_map_t; + struct VlanInfo { sai_object_id_t vlan_oid = 0; sai_vlan_id_t vlan_id = 0; sai_object_id_t host_intf_id = SAI_NULL_OBJECT_ID; + sai_vlan_flood_control_type_t uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + sai_vlan_flood_control_type_t bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + endpoint_ip_l2mc_group_member_map_t l2mc_members; }; struct SystemPortInfo diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 81866a9ae1..9e25694dac 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -42,6 +42,7 @@ extern sai_acl_api_t* sai_acl_api; extern sai_queue_api_t *sai_queue_api; extern sai_object_id_t gSwitchId; extern sai_fdb_api_t *sai_fdb_api; +extern sai_l2mc_group_api_t *sai_l2mc_group_api; extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern CrmOrch *gCrmOrch; @@ -448,6 +449,45 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector supported_flood_control_types(max_flood_control_types, 0); + sai_s32_list_t values; + values.count = max_flood_control_types; + values.list = supported_flood_control_types.data(); + + if (sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_VLAN, + SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE, + &values) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support unknown unicast flood control types"); + } + else + { + for (uint32_t idx = 0; idx < values.count; idx++) + { + uuc_sup_flood_control_type.insert(static_cast(values.list[idx])); + } + } + + + supported_flood_control_types.assign(max_flood_control_types, 0); + values.count = max_flood_control_types; + values.list = supported_flood_control_types.data(); + + if (sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_VLAN, + SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE, + &values) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support broadcast flood control types"); + } + else + { + for (uint32_t idx = 0; idx < values.count; idx++) + { + bc_sup_flood_control_type.insert(static_cast(values.list[idx])); + } + } + /* Get default 1Q bridge and default VLAN */ vector attrs; attr.id = SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID; @@ -4273,8 +4313,10 @@ bool PortsOrch::addVlan(string vlan_alias) sai_vlan_id_t vlan_id = (uint16_t)stoi(vlan_alias.substr(4)); sai_attribute_t attr; + attr.id = SAI_VLAN_ATTR_VLAN_ID; attr.value.u16 = vlan_id; + sai_status_t status = sai_vlan_api->create_vlan(&vlan_oid, gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) @@ -4292,6 +4334,8 @@ bool PortsOrch::addVlan(string vlan_alias) Port vlan(vlan_alias, Port::VLAN); vlan.m_vlan_info.vlan_oid = vlan_oid; vlan.m_vlan_info.vlan_id = vlan_id; + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; vlan.m_members = set(); m_portList[vlan_alias] = vlan; m_port_ref_count[vlan_alias] = 0; @@ -4372,7 +4416,127 @@ bool PortsOrch::getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan) return false; } -bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) +bool PortsOrch::addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + sai_status_t status; + sai_attribute_t attr; + + if (vlan.m_vlan_info.uuc_flood_type != SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc flood type combined " + " to vlan %hu for unknown unicast flooding", vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + } + + if (vlan.m_vlan_info.bc_flood_type != SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc flood type combined " + " to vlan %hu for broadcast flooding", vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + } + + if (vlan.m_vlan_info.l2mc_group_id == SAI_NULL_OBJECT_ID) + { + status = sai_l2mc_group_api->create_l2mc_group(&l2mc_group_id, gSwitchId, 0, NULL); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create l2mc flood group"); + return false; + } + + if (vlan.m_vlan_info.uuc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP; + attr.value.oid = l2mc_group_id; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc group %" PRIx64 + " to vlan %hu for unknown unicast flooding", + l2mc_group_id, vlan.m_vlan_info.vlan_id); + return false; + } + } + if (vlan.m_vlan_info.bc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP; + attr.value.oid = l2mc_group_id; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc group %" PRIx64 + " to vlan %hu for broadcast flooding", + l2mc_group_id, vlan.m_vlan_info.vlan_id); + return false; + } + } + vlan.m_vlan_info.l2mc_group_id = l2mc_group_id; + m_portList[vlan.m_alias] = vlan; + } + + vector attrs; + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID; + attr.value.oid = vlan.m_vlan_info.l2mc_group_id; + attrs.push_back(attr); + + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID; + attr.value.oid = port.m_bridge_port_id; + attrs.push_back(attr); + + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_ENDPOINT_IP; + IpAddress remote = IpAddress(end_point_ip); + sai_ip_address_t ipaddr; + if (remote.isV4()) + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + ipaddr.addr.ip4 = remote.getV4Addr(); + } + else + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(ipaddr.addr.ip6, remote.getV6Addr(), sizeof(ipaddr.addr.ip6)); + } + attr.value.ipaddr = ipaddr; + attrs.push_back(attr); + + sai_object_id_t l2mc_group_member = SAI_NULL_OBJECT_ID; + status = sai_l2mc_group_api->create_l2mc_group_member(&l2mc_group_member, gSwitchId, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create l2mc group member for adding tunnel %s to vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.l2mc_members[end_point_ip] = l2mc_group_member; + m_portList[vlan.m_alias] = vlan; + return true; +} + +bool PortsOrch::createVlanMember(Port &vlan, Port &port, sai_vlan_tagging_mode_t &sai_tagging_mode, + sai_object_id_t &vlan_member_id) { SWSS_LOG_ENTER(); @@ -4387,19 +4551,10 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) attr.value.oid = port.m_bridge_port_id; attrs.push_back(attr); - sai_vlan_tagging_mode_t sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; attr.id = SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE; - if (tagging_mode == "untagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_UNTAGGED; - else if (tagging_mode == "tagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; - else if (tagging_mode == "priority_tagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED; - else assert(false); attr.value.s32 = sai_tagging_mode; attrs.push_back(attr); - sai_object_id_t vlan_member_id; sai_status_t status = sai_vlan_api->create_vlan_member(&vlan_member_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -4413,6 +4568,40 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) } SWSS_LOG_NOTICE("Add member %s to VLAN %s vid:%hu pid%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, port.m_port_id); + return true; + +} + +bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t vlan_member_id; + if (!end_point_ip.empty()) + { + if ((uuc_sup_flood_control_type.find(SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + == uuc_sup_flood_control_type.end()) || + (bc_sup_flood_control_type.find(SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + == bc_sup_flood_control_type.end())) + { + SWSS_LOG_ERROR("Flood group with end point ip is not supported"); + return false; + } + return addVlanFloodGroups(vlan, port, end_point_ip); + } + + sai_vlan_tagging_mode_t sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; + if (tagging_mode == "untagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_UNTAGGED; + else if (tagging_mode == "tagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; + else if (tagging_mode == "priority_tagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED; + else assert(false); + if(!createVlanMember(vlan, port, sai_tagging_mode, vlan_member_id)) + { + return false; + } /* Use untagged VLAN as pvid of the member port */ if (sai_tagging_mode == SAI_VLAN_TAGGING_MODE_UNTAGGED) @@ -4436,10 +4625,101 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) return true; } -bool PortsOrch::removeVlanMember(Port &vlan, Port &port) +bool PortsOrch::removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip) { SWSS_LOG_ENTER(); + sai_status_t status; + + if(vlan.m_vlan_info.l2mc_members.find(end_point_ip) == vlan.m_vlan_info.l2mc_members.end()) + { + SWSS_LOG_NOTICE("End point ip %s is not part of vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return true; + } + + status = sai_l2mc_group_api->remove_l2mc_group_member(vlan.m_vlan_info.l2mc_members[end_point_ip]); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove end point ip %s from vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.l2mc_members.erase(end_point_ip); + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + sai_attribute_t attr; + + if (vlan.m_vlan_info.l2mc_members.empty()) + { + if (vlan.m_vlan_info.uuc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP; + attr.value.oid = SAI_NULL_OBJECT_ID; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set null l2mc group " + " to vlan %hu for unknown unicast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set flood control type all" + " to vlan %hu for unknown unicast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + if (vlan.m_vlan_info.bc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP; + attr.value.oid = SAI_NULL_OBJECT_ID; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set null l2mc group " + " to vlan %hu for broadcast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set flood control type all" + " to vlan %hu for broadcast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + status = sai_l2mc_group_api->remove_l2mc_group(vlan.m_vlan_info.l2mc_group_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove l2mc group %" PRIx64, l2mc_group_id); + return false; + } + vlan.m_vlan_info.l2mc_group_id = SAI_NULL_OBJECT_ID; + } + return true; +} + +bool PortsOrch::removeVlanMember(Port &vlan, Port &port, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + if (!end_point_ip.empty()) + { + return removeVlanEndPointIp(vlan, port, end_point_ip); + } sai_object_id_t vlan_member_id; sai_vlan_tagging_mode_t sai_tagging_mode; auto vlan_member = port.m_vlan_members.find(vlan.m_vlan_info.vlan_id); @@ -4483,8 +4763,16 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) return true; } -bool PortsOrch::isVlanMember(Port &vlan, Port &port) +bool PortsOrch::isVlanMember(Port &vlan, Port &port, string end_point_ip) { + if (!end_point_ip.empty()) + { + if (vlan.m_vlan_info.l2mc_members.find(end_point_ip) != vlan.m_vlan_info.l2mc_members.end()) + { + return true; + } + return false; + } if (vlan.m_members.find(port.m_alias) == vlan.m_members.end()) return false; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 6a92b9d9dd..043a6d2912 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -139,9 +139,12 @@ class PortsOrch : public Orch, public Subject bool removeTunnel(Port tunnel); bool addBridgePort(Port &port); bool removeBridgePort(Port &port); - bool addVlanMember(Port &vlan, Port &port, string& tagging_mode); - bool removeVlanMember(Port &vlan, Port &port); - bool isVlanMember(Port &vlan, Port &port); + bool addVlanMember(Port &vlan, Port &port, string& tagging_mode, string end_point_ip = ""); + bool removeVlanMember(Port &vlan, Port &port, string end_point_ip = ""); + bool isVlanMember(Port &vlan, Port &port, string end_point_ip = ""); + bool addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip); + bool createVlanMember(Port &vlan, Port &port, sai_vlan_tagging_mode_t &sai_tagging_mode, sai_object_id_t &vlan_member_id); + bool removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip); string m_inbandPortName = ""; bool isInbandPort(const string &alias); @@ -216,6 +219,9 @@ class PortsOrch : public Orch, public Subject unordered_map m_portOidToIndex; map m_port_ref_count; unordered_set m_pendingPortSet; + uint32_t max_flood_control_types = 4; + set uuc_sup_flood_control_type; + set bc_sup_flood_control_type; NotificationConsumer* m_portStatusNotificationConsumer; diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 0f688a64d0..5e7940b4ec 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -2043,6 +2043,10 @@ bool RouteOrch::createRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH bool status = false; int ip_refcnt = 0; + if (!tunnel_orch->dipTunnelsUsed()) + { + return true; + } status = tunnel_orch->addTunnelUser(nextHop.ip_address.to_string(), nextHop.vni, 0, TUNNEL_USER_IP, vrf_id); auto vtep_ptr = evpn_orch->getEVPNVtep(); @@ -2063,6 +2067,10 @@ bool RouteOrch::deleteRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH bool status = false; int ip_refcnt = 0; + if (!tunnel_orch->dipTunnelsUsed()) + { + return true; + } status = tunnel_orch->delTunnelUser(nextHop.ip_address.to_string(), nextHop.vni, 0, TUNNEL_USER_IP, vrf_id); auto vtep_ptr = evpn_orch->getEVPNVtep(); diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index dac7b1e92f..e071c9e834 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -64,6 +64,7 @@ sai_debug_counter_api_t* sai_debug_counter_api; sai_nat_api_t* sai_nat_api; sai_system_port_api_t* sai_system_port_api; sai_macsec_api_t* sai_macsec_api; +sai_l2mc_group_api_t* sai_l2mc_group_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -185,6 +186,7 @@ void initSaiApi() sai_api_query(SAI_API_NAT, (void **)&sai_nat_api); sai_api_query(SAI_API_SYSTEM_PORT, (void **)&sai_system_port_api); sai_api_query(SAI_API_MACSEC, (void **)&sai_macsec_api); + sai_api_query(SAI_API_L2MC_GROUP, (void **)&sai_l2mc_group_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -216,6 +218,7 @@ void initSaiApi() sai_log_set((sai_api_t)SAI_API_NAT, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SYSTEM_PORT, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_MACSEC, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_L2MC_GROUP, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location, const std::string &record_filename) diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 6e25454f13..e7e6f84bbb 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -1520,6 +1520,10 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) return; } + if (isSrcVtepTunnel(tunnelPort)) + { + return; + } getTunnelDIPFromPort(tunnelPort, remote_vtep); //If there are IMR/IP routes to the remote VTEP then ignore this call @@ -1527,7 +1531,7 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) if (refcnt > 0) { SWSS_LOG_INFO("Tunnel bridge port not removed. remote = %s refcnt = %d", - remote_vtep.c_str(), refcnt); + remote_vtep.c_str(), refcnt); return; } @@ -1536,7 +1540,7 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) if (!ret) { SWSS_LOG_ERROR("Remove Bridge port failed for remote = %s fdbcount = %d", - remote_vtep.c_str(), tunnelPort.m_fdb_count); + remote_vtep.c_str(), tunnelPort.m_fdb_count); return; } gPortsOrch->removeTunnel(tunnelPort); @@ -1544,20 +1548,36 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) // Remove DIP Tunnel HW vtep_ptr->deleteDynamicDIPTunnel(remote_vtep, TUNNEL_USER_IMR, false); SWSS_LOG_NOTICE("diprefcnt for remote %s = %d", - remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); - + remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); // Remove SIP Tunnel HW which might be pending on delete vtep_ptr->deletePendingSIPTunnel(); return ; } -std::string VxlanTunnelOrch::getTunnelPortName(const std::string& remote_vtep) +std::string VxlanTunnelOrch::getTunnelPortName(const std::string& vtep, bool local) { - std::string tunnelPortName = "Port_EVPN_" + remote_vtep; + + std::string tunnelPortName; + if (local) + { + tunnelPortName = "Port_SRC_VTEP_" + vtep; + } + else + { + tunnelPortName = "Port_EVPN_" + vtep; + } return tunnelPortName; } +bool VxlanTunnelOrch::isSrcVtepTunnel(Port& tunnelPort) +{ + string tunnel_port_name = tunnelPort.m_alias; + string prefix = "Port_SRC_VTEP_"; + return (tunnel_port_name.compare(0, prefix.length(), prefix) == 0); +} + + void VxlanTunnelOrch::getTunnelNameFromDIP(const string& dip, string& tunnel_name) { tunnel_name = "EVPN_" + dip; @@ -1645,9 +1665,9 @@ void VxlanTunnelOrch::addRemoveStateTableEntry(string tunnel_name, } } -bool VxlanTunnelOrch::getTunnelPort(const std::string& remote_vtep,Port& tunnelPort) +bool VxlanTunnelOrch::getTunnelPort(const std::string& vtep,Port& tunnelPort, bool local) { - auto port_tunnel_name = getTunnelPortName(remote_vtep); + auto port_tunnel_name = getTunnelPortName(vtep, local); bool ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); @@ -1717,6 +1737,15 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) TUNNELMAP_SET_VLAN(mapper_list); TUNNELMAP_SET_VRF(mapper_list); tunnel_obj->createTunnelHw(mapper_list,TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); + Port tunPort; + auto src_vtep = tunnel_obj->getSrcIP().to_string(); + if (!tunnel_orch->getTunnelPort(src_vtep, tunPort, true)) + { + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + gPortsOrch->addTunnel(port_tunnel_name, tunnel_obj->getTunnelId(), false); + gPortsOrch->getPort(port_tunnel_name,tunPort); + gPortsOrch->addBridgePort(tunPort); + } } const auto tunnel_map_id = tunnel_obj->getDecapMapId(TUNNEL_MAP_T_VLAN); @@ -1807,6 +1836,28 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) // then mark it as pending for delete. if (tunnel_obj->getDipTunnelCnt() == 0) { + Port tunnelPort; + auto src_vtep = tunnel_obj->getSrcIP().to_string(); + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + bool ret; + + ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); + return true; + } + + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + port_tunnel_name.c_str(), tunnelPort.m_fdb_count); + return true; + } + + gPortsOrch->removeTunnel(tunnelPort); + uint8_t mapper_list=0; TUNNELMAP_SET_VLAN(mapper_list); TUNNELMAP_SET_VRF(mapper_list); @@ -1977,7 +2028,7 @@ bool VxlanVrfMapOrch::delOperation(const Request& request) //------------------- EVPN_REMOTE_VNI Table --------------------------// -bool EvpnRemoteVniOrch::addOperation(const Request& request) +bool EvpnRemoteVnip2pOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); @@ -2044,7 +2095,7 @@ bool EvpnRemoteVniOrch::addOperation(const Request& request) return true; } -bool EvpnRemoteVniOrch::delOperation(const Request& request) +bool EvpnRemoteVnip2pOrch::delOperation(const Request& request) { bool ret; @@ -2120,6 +2171,135 @@ bool EvpnRemoteVniOrch::delOperation(const Request& request) return ret; } +bool EvpnRemoteVnip2mpOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + EvpnNvoOrch* evpn_orch = gDirectory.get(); + // Extract end point ip + auto end_point_ip = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = (sai_vlan_id_t) stoi(vlan_name.substr(4)); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port tunnelPort, vlanPort; + auto vtep_ptr = evpn_orch->getEVPNVtep(); + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI add: Source VTEP not found. remote=%s vid=%d", + end_point_ip.c_str(),vlan_id); + return true; + } + + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return false; + } + + auto src_vtep = vtep_ptr->getSrcIP().to_string(); + if (tunnel_orch->getTunnelPort(src_vtep,tunnelPort, true)) + { + SWSS_LOG_INFO("Vxlan tunnelPort exists: %s", src_vtep.c_str()); + + if (gPortsOrch->isVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("Remote end point %s already member of vid %d", + end_point_ip.c_str(),vlan_id); + vtep_ptr->increment_spurious_imr_add(end_point_ip); + return true; + } + } + else + { + SWSS_LOG_WARN("Vxlan tunnelPort doesn't exist: %s", src_vtep.c_str()); + return false; + } + + // SAI Call to add tunnel to the VLAN flood domain + + string tagging_mode = "untagged"; + gPortsOrch->addVlanMember(vlanPort, tunnelPort, tagging_mode, end_point_ip); + + SWSS_LOG_INFO("end_point_ip=%s vni=%d vlanid=%d ", + end_point_ip.c_str(), vni_id, vlan_id); + + return true; +} + +bool EvpnRemoteVnip2mpOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + // Extract end point ip + auto end_point_ip = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = (sai_vlan_id_t)stoi(vlan_name.substr(4)); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= 1<<24) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + // SAI Call to add tunnel to the VLAN flood domain + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port vlanPort, tunnelPort; + EvpnNvoOrch* evpn_orch = gDirectory.get(); + + auto vtep_ptr = evpn_orch->getEVPNVtep(); + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI add: VTEP not found. remote=%s vid=%d", + end_point_ip.c_str(),vlan_id); + return true; + } + + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return true; + } + + auto src_vtep = vtep_ptr->getSrcIP().to_string(); + if (!tunnel_orch->getTunnelPort(src_vtep,tunnelPort,true)) + { + SWSS_LOG_WARN("RemoteVniDel getTunnelPort Fails: %s", src_vtep.c_str()); + return true; + } + + + if (!gPortsOrch->isVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("marking it as spurious tunnelPort %s not a member of vid %d", + end_point_ip.c_str(), vlan_id); + vtep_ptr->increment_spurious_imr_del(end_point_ip); + return true; + } + + if (!gPortsOrch->removeVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("RemoteVniDel remove vlan member fails: vlan:%hu ip %s", + vlan_id, end_point_ip.c_str()); + return false; + } + + return true; +} + //------------------- EVPN_NVO Table --------------------------// bool EvpnNvoOrch::addOperation(const Request& request) diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index edc65d97fe..c939fd4aa3 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -171,6 +171,10 @@ class VxlanTunnel return ids_.tunnel_term_id; } + IpAddress getSrcIP() + { + return src_ip_; + } void updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nhId); bool removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni); @@ -241,9 +245,9 @@ typedef std::map VTEPTable; class VxlanTunnelOrch : public Orch2 { public: - VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName) : + VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName, bool dipTunnelsSupported) : Orch2(db, tableName, request_), - m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME) + m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME), dip_tunnels_used(dipTunnelsSupported) {} @@ -296,7 +300,7 @@ class VxlanTunnelOrch : public Orch2 bool removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); - bool getTunnelPort(const std::string& remote_vtep,Port& tunnelPort); + bool getTunnelPort(const std::string& vtep,Port& tunnelPort, bool local=false); bool addTunnelUser(string remote_vtep, uint32_t vni_id, uint32_t vlan, tunnel_user_t usr, @@ -310,7 +314,8 @@ class VxlanTunnelOrch : public Orch2 void addRemoveStateTableEntry(const string, IpAddress&, IpAddress&, tunnel_creation_src_t, bool); - std::string getTunnelPortName(const std::string& remote_vtep); + std::string getTunnelPortName(const std::string& vtep, bool local=false); + bool isSrcVtepTunnel(Port& tunnelPort); void getTunnelNameFromDIP(const string& dip, string& tunnel_name); void getTunnelNameFromPort(string& tunnel_portname, string& tunnel_name); void getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep); @@ -338,6 +343,10 @@ class VxlanTunnelOrch : public Orch2 vxlan_vni_vlan_map_table_.erase(vni); } + bool dipTunnelsUsed(void) + { + return dip_tunnels_used; + } private: @@ -349,6 +358,7 @@ class VxlanTunnelOrch : public Orch2 VxlanVniVlanMapTable vxlan_vni_vlan_map_table_; VTEPTable vtep_table_; Table m_stateVxlanTable; + bool dip_tunnels_used; }; const request_description_t vxlan_tunnel_map_request_description = { @@ -445,10 +455,23 @@ class EvpnRemoteVniRequest : public Request EvpnRemoteVniRequest() : Request(evpn_remote_vni_request_description, ':') { } }; -class EvpnRemoteVniOrch : public Orch2 +class EvpnRemoteVnip2pOrch : public Orch2 +{ +public: + EvpnRemoteVnip2pOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + EvpnRemoteVniRequest request_; +}; + +class EvpnRemoteVnip2mpOrch : public Orch2 { public: - EvpnRemoteVniOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + EvpnRemoteVnip2mpOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } private: diff --git a/tests/evpn_tunnel.py b/tests/evpn_tunnel.py new file mode 100644 index 0000000000..1c1f41a2a2 --- /dev/null +++ b/tests/evpn_tunnel.py @@ -0,0 +1,1143 @@ +from swsscommon import swsscommon +import time +import json +import random +from pytest import * +from pprint import pprint + + +class VxlanEvpnHelper(object): + def create_entry(self, tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + def create_entry_tbl(self, db, table, key, pairs): + tbl = swsscommon.Table(db, table) + self.create_entry(tbl, key, pairs) + + def delete_entry_tbl(self, db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + + def create_entry_pst(self, db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + self.create_entry(tbl, key, pairs) + + def delete_entry_pst(self, db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + + def how_many_entries_exist(self, db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + def get_exist_entries(self, dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + + def get_created_entry(self, db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + + def get_created_entries(self, db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + new_entries.sort() + return new_entries + + def get_deleted_entries(self, db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + old_entries = list(existed_entries - entries) + assert len(old_entries) == count, "Wrong number of deleted entries." + old_entries.sort() + return old_entries + + def check_object(self, db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + if name in expected_attributes: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + def get_key_with_attr(self, db, table, expected_attributes ): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + retkey = list() + #assert key in keys, "The desired key is not presented" + + for key in keys: + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + num_match = 0 + for name, value in fvs: + if name in expected_attributes: + if expected_attributes[name] == value: + num_match += 1 + if num_match == len(expected_attributes): + retkey.append(key) + + return retkey + + def check_deleted_object(self, db, table, key): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key not in keys, "The desired key is not removed" + + +''' + def entries(self, db, table): + tbl = swsscommon.Table(self, db, table) + return set(tbl.getKeys()) + + def get_default_vr_id(self, dvs): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert len(keys) == 1, "Wrong number of virtual routers found" + + return keys[0] +''' + +class VxlanTunnel(object): + + ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" + ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" + ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" + ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" + ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" + ASIC_NEXT_HOP_GRP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" + ASIC_NEXT_HOP_GRP_MEMBERS = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" + + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_map_vrf_entry_ids = set() + tunnel_ids = set() + tunnel_term_ids = set() + bridgeport_ids = set() + tunnel_map_map = {} + tunnel = {} + tunnel_appdb = {} + tunnel_term = {} + map_entry_map = {} + dip_tunnel_map = {} + dip_tun_state_map = {} + diptunterm_map = {} + bridgeport_map = {} + vlan_id_map = {} + vlan_member_map = {} + l2mcgroup_member_map = {} + l2mcgroup_map = {} + vr_map = {} + vnet_vr_ids = set() + nh_ids = {} + nh_grp_id = set() + nh_grp_member_id = set() + route_id = {} + helper = None + switch_mac = None + + + def __init__(self): + self.helper = VxlanEvpnHelper() + + def create_evpn_nvo(self, dvs, nvoname, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("source_vtep", tnl_name), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_EVPN_NVO", nvoname, + attrs, + ) + + def remove_evpn_nvo(self, dvs, nvoname): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + self.helper.delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) + + def create_vxlan_tunnel(self, dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("src_ip", src_ip), + ] + + if not skip_dst_ip: + attrs.append(("dst_ip", dst_ip)) + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_TUNNEL", name, + attrs, + ) + + def create_vxlan_tunnel_map(self, dvs, tnl_name, map_name, vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), + attrs, + ) + + def create_evpn_remote_vni(self, dvs, vlan_id, remote_vtep, vnid): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.create_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), + [ + ("vni", vnid), + ], + ) + time.sleep(2) + + def remove_vxlan_tunnel(self, dvs, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create the VXLAN tunnel Term entry in Config DB + self.helper.delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL", tnl_name, + ) + + def remove_vxlan_tunnel_map(self, dvs, tnl_name, map_name,vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), + ) + + def remove_evpn_remote_vni(self, dvs, vlan_id, remote_vtep ): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.delete_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), + ) + time.sleep(2) + + def create_vxlan_vrf_tunnel_map(self, dvs, vrfname, vni_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ] + + # create the VXLAN VRF tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VRF", vrfname, + attrs, + ) + + def remove_vxlan_vrf_tunnel_map(self, dvs, vrfname): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", "0"), + ] + + # remove the VXLAN VRF tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VRF", vrfname, + attrs, + ) + + def create_vlan1(self, dvs, vlan_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + self.helper.create_entry_tbl( + conf_db, + "VLAN", vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + + def create_vrf_routes(self, dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + attrs = [ + ("nexthop", endpoint), + ("ifname", ifname), + ] + + if vni: + attrs.append(('vni_label', vni)) + + if mac: + attrs.append(('router_mac', mac)) + + self.helper.create_entry_pst( + app_db, + "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix), + attrs, + ) + + time.sleep(2) + + def delete_vrf_routes(self, dvs, prefix, vrf_name): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + self.helper.delete_entry_pst(app_db, "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix)) + + time.sleep(2) + + def create_vrf_routes_ecmp(self, dvs, prefix, vrf_name, ecmp_nexthop_attributes): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + self.helper.create_entry_pst( + app_db, + "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix), + ecmp_nexthop_attributes, + ) + + time.sleep(2) + + def create_vlan(self, dvs, vlan_name, vlan_ids): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + self.helper.create_entry_tbl( + conf_db, + "VLAN", vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + + time.sleep(1) + + vlan_oid = self.helper.get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_ids) + + self.helper.check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_oid, + { + "SAI_VLAN_ATTR_VLAN_ID": vlan_id, + } + ) + + return vlan_oid + + def remove_vlan(self, dvs, vlan): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN") + tbl._del("Vlan" + vlan) + time.sleep(1) + + def create_vlan_member(self, dvs, vlan, interface, tagging_mode="untagged"): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", tagging_mode)]) + tbl.set("Vlan" + vlan + "|" + interface, fvs) + time.sleep(1) + + def remove_vlan_member(self, dvs, vlan, interface): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") + tbl._del("Vlan" + vlan + "|" + interface) + time.sleep(1) + + + def create_vlan_interface(self, dvs, vlan_name, ifname, vrf_name, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create a vlan member in config db + self.helper.create_entry_tbl( + conf_db, + "VLAN_MEMBER", "%s|%s" % (vlan_name, ifname), + [ + ("tagging_mode", "untagged"), + ], + ) + + time.sleep(1) + + # create vlan interface in config db + self.helper.create_entry_tbl( + conf_db, + "VLAN_INTERFACE", vlan_name, + [ + ("vrf_name", vrf_name), + ], + ) + + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.create_entry_pst( + app_db, + "INTF_TABLE", vlan_name, + [ + ("vrf_name", vrf_name), + ], + ) + time.sleep(2) + + self.helper.create_entry_tbl( + conf_db, + "VLAN_INTERFACE", "%s|%s" % (vlan_name, ipaddr), + [ + ("family", "IPv4"), + ], + ) + + time.sleep(2) + + def delete_vlan_interface(self, dvs, ifname, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + self.helper.delete_entry_tbl(conf_db, "VLAN_INTERFACE", "%s|%s" % (ifname, ipaddr)) + time.sleep(2) + + self.helper.delete_entry_tbl(conf_db, "VLAN_INTERFACE", ifname) + time.sleep(2) + + def get_switch_mac(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH') + + entries = tbl.getKeys() + mac = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_SWITCH_ATTR_SRC_MAC_ADDRESS': + mac = value + break + else: + assert False, 'Don\'t found switch mac' + + return mac + + def fetch_exist_entries(self, dvs): + self.tunnel_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) + self.tunnel_map_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) + self.tunnel_map_entry_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) + self.tunnel_term_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) + self.bridgeport_ids = self.helper.get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) + self.vnet_vr_ids = self.helper.get_exist_entries(dvs, self.ASIC_VRF_TABLE) + self.rifs = self.helper.get_exist_entries(dvs, self.ASIC_RIF_TABLE) + self.routes = self.helper.get_exist_entries(dvs, self.ASIC_ROUTE_ENTRY) + self.nhops = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP) + self.nhop_grp = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP) + self.nhop_grp_members = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP_MEMBERS) + + if self.switch_mac is None: + self.switch_mac = self.get_switch_mac(dvs) + + + def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + for x in range(len(vidlist)): + status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) + assert status == False, "SIP Tunnel Map entry not deleted" + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode != 0, "Kernel device not deleted" + + def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) + assert len(ret) > 0, "SIP TunnelMap entry not created" + assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" + self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode == 0, "Kernel device not created" + + + def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name, sip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") + status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from APP_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) + status, fvs = tbl.get(self.tunnel_term[tunnel_name]) + assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.tunnel[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) + assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) + assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) + assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) + assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) + status, fvs = tbl.get(self.bridgeport_map[sip]) + assert status == False, "Tunnel bridgeport entry not deleted" + + def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + tunnel_id = self.helper.get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) + tunnel_term_id = self.helper.get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + } + ) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + } + ) + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + } + ) + + expected_attributes = { + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, + } + + if not skip_dst_ip: + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) + + expected_siptnl_attributes = { + 'src_ip': src_ip, + } + + if not skip_dst_ip: + expected_siptnl_attributes['dst_ip'] = dst_ip + + ret = self.helper.get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) + assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.tunnel_appdb[tunnel_name] = ret[0] + + expected_bridgeport_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) + assert len(ret) > 0, "Bridgeport entry not created" + assert len(ret) == 1, "More than 1 bridgeport entry created" + self.bridgeport_map[src_ip] = ret[0] + self.tunnel_map_ids.update(tunnel_map_id) + self.tunnel_ids.add(tunnel_id) + self.tunnel_term_ids.add(tunnel_term_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_id + self.tunnel[tunnel_name] = tunnel_id + self.tunnel_term[tunnel_name] = tunnel_term_id + + def check_vxlan_dip_tunnel_delete(self, dvs, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') + status, fvs = tbl.get(self.dip_tun_state_map[dip]) + assert status == False, "State Table entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.dip_tunnel_map[dip]) + assert status == False, "Tunnel entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) + status, fvs = tbl.get(self.bridgeport_map[dip]) + assert status == False, "Tunnel bridgeport entry not deleted" + + def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + expected_state_attributes = { + 'src_ip': src_ip, + 'dst_ip': dip, + 'tnl_src': 'EVPN', + } + + ret = self.helper.get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) + assert len(ret) > 0, "Tunnel Statetable entry not created" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.dip_tun_state_map[dip] = ret[0] + + + tunnel_map_id = self.tunnel_map_map[vtep_name] + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + + print(decapstr) + print(encapstr) + + expected_tun_attributes = { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, + } + + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) + assert len(ret) > 0, "Tunnel entry not created" + assert len(ret) == 1, "More than 1 tunnel entry created" + + self.dip_tunnel_map[dip] = ret[0] + tunnel_id = ret[0] + + expected_bridgeport_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) + assert len(ret) > 0, "Bridgeport entry not created" + assert len(ret) == 1, "More than 1 bridgeport entry created" + + self.bridgeport_map[dip] = ret[0] + + def check_vlan_extension_delete(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') + status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) + assert status == False, "VLAN Member entry not deleted" + + def check_vlan_extension_delete_p2mp(self, dvs, vlan_name, sip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_L2MC_GROUP_MEMBER') + status, fvs = tbl.get(self.l2mcgroup_member_map[dip+vlan_name]) + assert status == False, "L2MC Group Member entry not deleted" + + def check_vlan_extension(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + expected_vlan_attributes = { + 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) + assert len(ret) > 0, "VLAN entry not created" + assert len(ret) == 1, "More than 1 VLAN entry created" + + self.vlan_id_map[vlan_name] = ret[0] + + expected_vlan_member_attributes = { + 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], + 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) + assert len(ret) > 0, "VLAN Member not created" + assert len(ret) == 1, "More than 1 VLAN member created" + self.vlan_member_map[dip+vlan_name] = ret[0] + + def check_vlan_extension_p2mp(self, dvs, vlan_name, sip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN') + expected_vlan_attributes = { + 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) + assert len(ret) > 0, "VLAN entry not created" + assert len(ret) == 1, "More than 1 VLAN entry created" + + self.vlan_id_map[vlan_name] = ret[0] + status, fvs = tbl.get(self.vlan_id_map[vlan_name]) + + print(fvs) + #assert 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE' in fvs, "Unknown unicast flood control type not set" + #assert 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE' in fvs, "Broadcast flood control type not set" + + uuc_flood_type = None + bc_flood_type = None + uuc_flood_group = None + bc_flood_group = None + + for attr,value in fvs: + if attr == 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE': + uuc_flood_type = value + elif attr == 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': + bc_flood_type = value + elif attr == 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP': + uuc_flood_group = value + elif attr == 'SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP': + bc_flood_group = value + + assert uuc_flood_type == 'SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED', "Unknown unicast flood control type is not combined" + assert bc_flood_type == 'SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED', "Broadcast flood control type is not combined" + assert uuc_flood_group == bc_flood_group, "Unexpected two flood groups in VLAN" + + + expected_l2mc_group_member_attributes = { + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID' : uuc_flood_group, + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_ENDPOINT_IP': dip, + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID': self.bridgeport_map[sip], + } + + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_L2MC_GROUP_MEMBER', expected_l2mc_group_member_attributes) + assert len(ret) > 0, "L2MC group Member not created" + assert len(ret) == 1, "More than 1 L2MC group member created" + self.l2mcgroup_member_map[dip+vlan_name] = ret[0] + + def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + time.sleep(2) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + def check_vxlan_tunnel_vrf_map_entry(self, dvs, tunnel_name, vrf_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created too early" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[3], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vrf_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[1]) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[2], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[2], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vrf_name].get('egr'), + } + ) + + self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[2]) + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + def check_vxlan_tunnel_vrf_map_entry_remove(self, dvs, tunnel_name, vrf_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tunnel_map_entry_id = self.helper.get_deleted_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0]) + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1]) + for vrf_map_id in self.tunnel_map_vrf_entry_ids: + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, vrf_map_id) + + def check_router_interface(self, dvs, name, vlan_oid, route_count): + # Check RIF in ingress VRF + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + expected_attr = { + "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": self.vr_map[name].get('ing'), + "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": self.switch_mac, + } + + if vlan_oid: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) + else: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) + + new_rif = self.helper.get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) + self.helper.check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) + + #IP2ME route will be created with every router interface + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, route_count) + + self.rifs.add(new_rif) + self.routes.update(new_route) + + def check_del_router_interface(self, dvs, name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + old_rif = self.helper.get_deleted_entries(asic_db, self.ASIC_RIF_TABLE, self.rifs, 1) + self.helper.check_deleted_object(asic_db, self.ASIC_RIF_TABLE, old_rif[0]) + + self.rifs.remove(old_rif[0]) + + def vrf_route_ids(self, dvs, vrf_name): + vr_set = set() + + vr_set.add(self.vr_map[vrf_name].get('ing')) + return vr_set + + def check_vrf_routes(self, dvs, prefix, vrf_name, endpoint, tunnel, mac="", vni=0, no_update=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vrf_route_ids(dvs, vrf_name) + count = len(vr_ids) + + # Check routes in ingress VRF + expected_attr = { + "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", + "SAI_NEXT_HOP_ATTR_IP": endpoint, + "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], + } + + if vni: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_VNI': vni}) + + if mac: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_MAC': mac}) + + if endpoint in self.nh_ids: + new_nh = self.nh_ids[endpoint] + else: + new_nh = self.helper.get_created_entry(asic_db, self.ASIC_NEXT_HOP, self.nhops) + self.nh_ids[endpoint] = new_nh + self.nhops.add(new_nh) + + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP, new_nh, expected_attr) + if no_update: + count = 0 + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + + if count: + self.route_id[vrf_name + ":" + prefix] = new_route + + #Check if the route is in expected VRF + asic_vrs = set() + for idx in range(count): + self.helper.check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], + { + "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nh, + } + ) + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + if count: + assert asic_vrs == vr_ids + + self.routes.update(new_route) + + return True + + def check_vrf_routes_ecmp(self, dvs, prefix, vrf_name, tunnel, nh_count, no_update=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vrf_route_ids(dvs, vrf_name) + count = len(vr_ids) + + new_nhg = self.helper.get_created_entry(asic_db, self.ASIC_NEXT_HOP_GRP, self.nhop_grp) + self.nh_grp_id.add(new_nhg) + + if no_update: + count = 0 + + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + if count: + self.route_id[vrf_name + ":" + prefix] = new_route + + #Check if the route is in expected VRF + asic_vrs = set() + for idx in range(count): + self.helper.check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], + { + "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nhg, + } + ) + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + if count: + assert asic_vrs == vr_ids + + self.routes.update(new_route) + + new_nhg_members = self.helper.get_created_entries(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, self.nhop_grp_members, nh_count) + + for idx in range(nh_count): + self.nh_grp_member_id.add(new_nhg_members[idx]) + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, new_nhg_members[idx], + { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": new_nhg, + } + ) + + nhid_list = list() + + nhg_member_tbl = swsscommon.Table(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS) + + for k in new_nhg_members: + (status, fvs) = nhg_member_tbl.get(k) + assert status + + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + nhid = v[1] + nhid_list.append(nhid) + + return nhid_list + + def check_add_tunnel_nexthop(self, dvs, nh_id, endpoint, tunnel, mac, vni): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + # Check routes in ingress VRF + expected_attr = { + "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", + "SAI_NEXT_HOP_ATTR_IP": endpoint, + "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], + "SAI_NEXT_HOP_ATTR_TUNNEL_VNI": vni, + "SAI_NEXT_HOP_ATTR_TUNNEL_MAC": mac, + } + + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP, nh_id, expected_attr) + + def check_del_tunnel_nexthop(self, dvs, vrf_name, endpoint, tunnel, mac="", vni=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + del_nh_ids = self.helper.get_deleted_entries(asic_db, self.ASIC_NEXT_HOP, self.nhops, 1) + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP, del_nh_ids[0]) + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP, self.nh_ids[endpoint]) + assert del_nh_ids[0] == self.nh_ids[endpoint] + self.nh_ids.pop(endpoint) + return True + + def check_vrf_routes_ecmp_nexthop_grp_del(self, dvs, nh_count): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + nh_grp_id_len = len(self.nh_grp_id) + assert nh_grp_id_len == 1 + + for nh_grp_id in self.nh_grp_id: + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP, nh_grp_id) + self.nh_grp_id.clear() + + nh_grp_member_id_len = len(self.nh_grp_member_id) + assert nh_grp_member_id_len == nh_count + + for nh_grp_member_id in self.nh_grp_member_id: + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, nh_grp_member_id) + + self.nh_grp_member_id.clear() + + def check_del_vrf_routes(self, dvs, prefix, vrf_name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + del_route = self.helper.get_deleted_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) + + for idx in range(1): + rt_key = json.loads(del_route[idx]) + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + self.helper.check_deleted_object(asic_db, self.ASIC_ROUTE_ENTRY, self.route_id[vrf_name + ":" + prefix]) + self.route_id.clear() + + return True + + def create_vrf(self, dvs, vrf_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + initial_entries = set(tbl.getKeys()) + + attrs = [ + ("vni", "0"), + ] + tbl = swsscommon.Table(conf_db, "VRF") + fvs = swsscommon.FieldValuePairs(attrs) + tbl.set(vrf_name, fvs) + time.sleep(2) + + tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + current_entries = set(tbl.getKeys()) + assert len(current_entries - initial_entries) == 1 + + new_vr_ids = self.helper.get_created_entries(asic_db, self.ASIC_VRF_TABLE, self.vnet_vr_ids, 1) + self.vnet_vr_ids.update(new_vr_ids) + self.vr_map[vrf_name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[0]} + + return list(current_entries - initial_entries)[0] + + def remove_vrf(self, dvs, vrf_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VRF") + tbl._del(vrf_name) + time.sleep(2) + + def is_vrf_attributes_correct(self, db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = set(tbl.getKeys()) + assert key in keys, "The created key wasn't found" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + # filter the fake 'NULL' attribute out + fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) + + attr_keys = {entry[0] for entry in fvs} + assert attr_keys == set(expected_attributes.keys()) + + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + diff --git a/tests/mock_tests/mock_orchagent_main.cpp b/tests/mock_tests/mock_orchagent_main.cpp index 62a03dc770..1dbb16032a 100644 --- a/tests/mock_tests/mock_orchagent_main.cpp +++ b/tests/mock_tests/mock_orchagent_main.cpp @@ -19,6 +19,7 @@ bool gSairedisRecord = true; bool gSwssRecord = true; bool gLogRotate = false; bool gSaiRedisLogRotate = false; +bool gP2PTunnelSupported = true; ofstream gRecordOfs; string gRecordFile; string gMySwitchType = "switch"; diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 587055b87e..c64fae327f 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -30,6 +30,7 @@ extern MacAddress gVxlanMacAddress; extern sai_object_id_t gSwitchId; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gUnderlayIfId; +extern bool gP2PTunnelSupported; extern SwitchOrch *gSwitchOrch; extern CrmOrch *gCrmOrch; diff --git a/tests/test_evpn_fdb.py b/tests/test_evpn_fdb.py index 0989815b9d..31d75535c7 100644 --- a/tests/test_evpn_fdb.py +++ b/tests/test_evpn_fdb.py @@ -1,118 +1,6 @@ from swsscommon import swsscommon import time - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - -def remove_mac(db, table, mac, vlan): - tbl = swsscommon.Table(db, table) - tbl._del("Vlan" + vlan + "|" + mac.lower()) - time.sleep(1) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_evpn_nvo(db, nvoname, tnlname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - attrs = [ - ("source_vtep", tnlname), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_EVPN_NVO", nvoname, - attrs, - ) - -def remove_evpn_nvo(db, nvoname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(db, name, src_ip): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_TUNNEL", name, - attrs, - ) - -def remove_vxlan_tunnel(db, tnlname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - db, - "VXLAN_TUNNEL", tnlname, - ) - -def create_vxlan_tunnel_map(db, tnlname, mapname, vni_id, vlan_id): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - attrs, - ) - -def remove_vxlan_tunnel_map(db, tnlname, mapname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - ) - -def create_evpn_remote_vni(db, vlan_id, remotevtep, vnid): - #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - [ - ("vni", vnid), - ], - ) - -def remove_evpn_remote_vni(db, vlan_id, remotevtep ): - #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - ) +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper def get_vxlan_p2p_tunnel_bp(db, remote_ip): tnl_id = None @@ -159,6 +47,8 @@ def get_vxlan_p2p_tunnel_bp(db, remote_ip): def test_evpnFdb(dvs, testlog): + vxlan_obj = VxlanTunnel() + helper = VxlanEvpnHelper() dvs.setup_db() dvs.runcmd("sonic-clear fdb all") @@ -168,7 +58,7 @@ def test_evpnFdb(dvs, testlog): switch_id = dvs.getSwitchOid() print("Switch_id="+str(switch_id)) - vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") # create vlan print("Creating Vlan3") @@ -176,7 +66,7 @@ def test_evpnFdb(dvs, testlog): dvs.create_vlan("3") time.sleep(2) - vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") assert vlan_after - vlan_before == 1, "The Vlan3 wasn't created" print("Vlan3 is created") @@ -185,8 +75,8 @@ def test_evpnFdb(dvs, testlog): assert vlan_oid_3 is not None, "Could not find Vlan_oid" print("Vlan-3 vlan_oid="+str(vlan_oid_3)) - bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + bp_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") print("Making Ethernet0 as a member of Vlan3") #dvs.runcmd("config vlan member add 3 Ethernet0") @@ -194,8 +84,8 @@ def test_evpnFdb(dvs, testlog): time.sleep(2) # check that the vlan information was propagated - bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") - vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + bp_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") assert bp_after - bp_before == 1, "The bridge port wasn't created" assert vm_after - vm_before == 1, "The vlan member wasn't added" @@ -207,30 +97,30 @@ def test_evpnFdb(dvs, testlog): #create SIP side of tunnel source_tnl_name = "source_vtep_name" source_tnl_ip = "7.7.7.7" - create_vxlan_tunnel(dvs.cdb, source_tnl_name, source_tnl_ip) + vxlan_obj.create_vxlan_tunnel(dvs, source_tnl_name, source_tnl_ip) time.sleep(1) nvo_name = "evpn_nvo" - create_evpn_nvo(dvs.cdb, nvo_name, source_tnl_name) + vxlan_obj.create_evpn_nvo(dvs, nvo_name, source_tnl_name) time.sleep(1) map_name_vlan_3 = "map_3_3" - create_vxlan_tunnel_map(dvs.cdb, source_tnl_name, map_name_vlan_3, "3", "Vlan3") + vxlan_obj.create_vxlan_tunnel_map(dvs, source_tnl_name, map_name_vlan_3, "3", "Vlan3") time.sleep(1) remote_ip_6 = "6.6.6.6" - create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_6, "3") + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_6, "3") remote_ip_8 = "8.8.8.8" - create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_8, "3") + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_8, "3") time.sleep(1) #UT-1 Evpn Mac add from remote when tunnels are already created mac = "52:54:00:25:06:E9" print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -260,7 +150,7 @@ def test_evpnFdb(dvs, testlog): #UT-2 Evpn Mac del from remote mac = "52:54:00:25:06:E9" print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - delete_entry_pst( + helper.delete_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() ) @@ -285,7 +175,7 @@ def test_evpnFdb(dvs, testlog): print("Creating Local dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 in APP-DB") # Create Dynamic MAC entry in APP DB - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -317,7 +207,7 @@ def test_evpnFdb(dvs, testlog): print("FDB Vlan3:"+mac+":Ethernet0 is created in STATE-DB") print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -357,7 +247,7 @@ def test_evpnFdb(dvs, testlog): #UT-4 Evpn Sticky Mac add from remote mac = "52:54:00:25:06:E9" print("Creating Evpn Sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -385,7 +275,7 @@ def test_evpnFdb(dvs, testlog): #UT-8 Evpn Mac add from remote when tunnels are already created mac = "52:54:00:25:06:E9" print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -415,7 +305,7 @@ def test_evpnFdb(dvs, testlog): tnl_bp_oid_8 = get_vxlan_p2p_tunnel_bp(dvs.adb, remote_ip_8) print("Creating Evpn FDB Vlan3:"+mac.lower()+":8.8.8.8 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -445,7 +335,7 @@ def test_evpnFdb(dvs, testlog): #UT-9 Local mac move (delete and learn) when remote is already added mac = "52:54:00:25:06:E9" print("Deleting FDB Vlan3:52-54-00-25-06-E9:8.8.8.8 in ASIC-DB") - delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") + helper.delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") fvp = swsscommon.FieldValuePairs() @@ -457,7 +347,7 @@ def test_evpnFdb(dvs, testlog): #raw_input("Check ASIC_DB.........") print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") - create_entry_tbl( + helper.create_entry_tbl( dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", [ diff --git a/tests/test_evpn_fdb_p2mp.py b/tests/test_evpn_fdb_p2mp.py new file mode 100644 index 0000000000..0aa8bd5ba0 --- /dev/null +++ b/tests/test_evpn_fdb_p2mp.py @@ -0,0 +1,392 @@ +from swsscommon import swsscommon +import time +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper + +DVS_FAKE_PLATFORM = "mellanox" + +def get_vxlan_p2mp_tunnel_bp(db, src_ip): + tnl_id = None + bp = None + print("src_ip = " + src_ip) + attributes = [("SAI_TUNNEL_ATTR_TYPE", "SAI_TUNNEL_TYPE_VXLAN"), + ("SAI_TUNNEL_ATTR_PEER_MODE", "SAI_TUNNEL_PEER_MODE_P2MP"), + ("SAI_TUNNEL_ATTR_ENCAP_SRC_IP", src_ip) + ] + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + attrs = dict(attributes) + num_match = 0 + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k in attrs and attrs[k] == v: + num_match += 1 + if num_match == len(attributes): + tnl_id = str(key) + break + else: + tnl_id = None + + print("tnl_id = "+str(tnl_id)) + if tnl_id != None: + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k == "SAI_BRIDGE_PORT_ATTR_TUNNEL_ID" and tnl_id == v: + bp = key + break + if bp != None: + break + else: + pass + print("bp = "+str(bp)) + return bp + + +def test_evpnFdbP2MP(dvs, testlog): + vxlan_obj = VxlanTunnel() + helper = VxlanEvpnHelper() + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + #Find switch_id + switch_id = dvs.getSwitchOid() + print("Switch_id="+str(switch_id)) + + vlan_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + + # create vlan + print("Creating Vlan3") + #dvs.runcmd("config vlan add 3") + dvs.create_vlan("3") + time.sleep(2) + + vlan_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + assert vlan_after - vlan_before == 1, "The Vlan3 wasn't created" + print("Vlan3 is created") + + # Find the vlan_oid to be used in DB communications + vlan_oid_3 = dvs.getVlanOid("3") + assert vlan_oid_3 is not None, "Could not find Vlan_oid" + print("Vlan-3 vlan_oid="+str(vlan_oid_3)) + + bp_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + print("Making Ethernet0 as a member of Vlan3") + #dvs.runcmd("config vlan member add 3 Ethernet0") + dvs.create_vlan_member("3", "Ethernet0") + time.sleep(2) + + # check that the vlan information was propagated + bp_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + print("Ethernet0 is a member of Vlan3") + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + #create SIP side of tunnel + source_tnl_name = "source_vtep_name" + source_tnl_ip = "7.7.7.7" + vxlan_obj.create_vxlan_tunnel(dvs, source_tnl_name, source_tnl_ip) + time.sleep(1) + + + nvo_name = "evpn_nvo" + vxlan_obj.create_evpn_nvo(dvs, nvo_name, source_tnl_name) + time.sleep(1) + + + map_name_vlan_3 = "map_3_3" + vxlan_obj.create_vxlan_tunnel_map(dvs, source_tnl_name, map_name_vlan_3, "3", "Vlan3") + time.sleep(1) + + + remote_ip_6 = "6.6.6.6" + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_6, "3") + remote_ip_8 = "8.8.8.8" + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_8, "3") + time.sleep(1) + + #UT-1 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + tnl_bp_oid_6 = get_vxlan_p2mp_tunnel_bp(dvs.adb, source_tnl_ip) + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + #UT-2 Evpn Mac del from remote + mac = "52:54:00:25:06:E9" + print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.delete_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() + ) + time.sleep(1) + + # check that the FDB entry is deleted from ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == False, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is deleted from ASIC-DB") + + time.sleep(1) + + #UT-3 Evpn Mac add from remote when local mac is already present + mac = "52:54:00:25:06:E9" + + print("Creating Local dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 in APP-DB") + # Create Dynamic MAC entry in APP DB + helper.create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + time.sleep(1) + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("Dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 is created in Asic-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is created in STATE-DB") + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + # check that the Local FDB entry is deleted from STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is deleted from STATE-DB") + + time.sleep(1) + + + #UT-4 Evpn Sticky Mac add from remote + mac = "52:54:00:25:06:E9" + print("Creating Evpn Sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "static"), + ("vni", "3") + ] + ) + time.sleep(1) + + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN Sticky FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + #raw_input("Check ASIC_DB.........") + + #UT-8 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + tnl_bp_oid_8 = get_vxlan_p2mp_tunnel_bp(dvs.adb, source_tnl_ip) + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":8.8.8.8 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_8), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_8), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_8)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_8+" is created in ASIC-DB") + + time.sleep(1) + + #UT-9 Local mac move (delete and learn) when remote is already added + mac = "52:54:00:25:06:E9" + print("Deleting FDB Vlan3:52-54-00-25-06-E9:8.8.8.8 in ASIC-DB") + helper.delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\""+mac+"\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+str(tnl_bp_oid_8)+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") + helper.create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+iface_2_bridge_port_id["Ethernet0"]+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + #raw_input("Check ASIC_DB.........") + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in ASIC-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:52:54:00:25:06:e9", + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in STATE-DB") + + + dvs.remove_vlan_member("3", "Ethernet0") + dvs.remove_vlan("3") diff --git a/tests/test_evpn_l3_vxlan.py b/tests/test_evpn_l3_vxlan.py index 6f541b20a6..6276830125 100644 --- a/tests/test_evpn_l3_vxlan.py +++ b/tests/test_evpn_l3_vxlan.py @@ -4,1007 +4,16 @@ import random import pytest from pprint import pprint - - -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) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_entry_pst(db, table, separator, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - - -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_exist_entries(dvs, table): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_created_entry(db, table, existed_entries): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == 1, "Wrong number of created entries." - return new_entries[0] - - -def get_created_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." - new_entries.sort() - return new_entries - -def get_deleted_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - old_entries = list(existed_entries - entries) - assert len(old_entries) == count, "Wrong number of deleted entries." - old_entries.sort() - return old_entries - -def get_default_vr_id(dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] - -def check_object(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key in keys, "The desired key is not presented" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - for name, value in fvs: - if name in expected_attributes: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - -def check_deleted_object(db, table, key): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key not in keys, "The desired key is not removed" - -def get_key_with_attr(db, table, expected_attributes ): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - retkey = list() - #assert key in keys, "The desired key is not presented" - - for key in keys: - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - num_match = 0 - for name, value in fvs: - if name in expected_attributes: - if expected_attributes[name] == value: - num_match += 1 - if num_match == len(expected_attributes): - retkey.append(key) - - return retkey - -def create_vrf_routes(dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attrs = [ - ("nexthop", endpoint), - ("ifname", ifname), - ] - - if vni: - attrs.append(('vni_label', vni)) - - if mac: - attrs.append(('router_mac', mac)) - - create_entry_pst( - app_db, - "ROUTE_TABLE", ':', "%s:%s" % (vrf_name, prefix), - attrs, - ) - - time.sleep(2) - -def delete_vrf_routes(dvs, prefix, vrf_name): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - delete_entry_pst(app_db, "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix)) - - time.sleep(2) - -def create_vrf_routes_ecmp(dvs, prefix, vrf_name, ecmp_nexthop_attributes): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - create_entry_pst( - app_db, - "ROUTE_TABLE", ':', "%s:%s" % (vrf_name, prefix), - ecmp_nexthop_attributes, - ) - - time.sleep(2) - -def create_vlan(dvs, vlan_name, vlan_ids): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - - time.sleep(1) - - vlan_oid = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_ids) - - check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_oid, - { - "SAI_VLAN_ATTR_VLAN_ID": vlan_id, - } - ) - - return vlan_oid - -def remove_vlan(dvs, vlan): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN") - tbl._del("Vlan" + vlan) - time.sleep(1) - -def create_vlan_member(dvs, vlan, interface, tagging_mode="untagged"): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", tagging_mode)]) - tbl.set("Vlan" + vlan + "|" + interface, fvs) - time.sleep(1) - -def remove_vlan_member(dvs, vlan, interface): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") - tbl._del("Vlan" + vlan + "|" + interface) - time.sleep(1) - -def create_vlan_interface(dvs, vlan_name, ifname, vrf_name, ipaddr): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create a vlan member in config db - create_entry_tbl( - conf_db, - "VLAN_MEMBER", '|', "%s|%s" % (vlan_name, ifname), - [ - ("tagging_mode", "untagged"), - ], - ) - - time.sleep(1) - - # create vlan interface in config db - create_entry_tbl( - conf_db, - "VLAN_INTERFACE", '|', vlan_name, - [ - ("vrf_name", vrf_name), - ], - ) - - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "INTF_TABLE", ':', vlan_name, - [ - ("vrf_name", vrf_name), - ], - ) - time.sleep(2) - - create_entry_tbl( - conf_db, - "VLAN_INTERFACE", '|', "%s|%s" % (vlan_name, ipaddr), - [ - ("family", "IPv4"), - ], - ) - - time.sleep(2) - - -def delete_vlan_interface(dvs, ifname, ipaddr): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - delete_entry_tbl(conf_db, "VLAN_INTERFACE", "%s|%s" % (ifname, ipaddr)) - time.sleep(2) - - delete_entry_tbl(conf_db, "VLAN_INTERFACE", ifname) - time.sleep(2) - -def create_evpn_nvo(dvs, nvoname, tnlname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("source_vtep", tnlname), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_EVPN_NVO", '|', nvoname, - attrs, - ) - -def remove_evpn_nvo(dvs, nvoname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(dvs, name, src_ip): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - -def create_vxlan_tunnel_map(dvs, tnlname, mapname, vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnlname, mapname), - attrs, - ) - - -def create_vxlan_vrf_tunnel_map(dvs, vrfname, vni_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ] - - # create the VXLAN VRF tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VRF", '|', vrfname, - attrs, - ) - -def remove_vxlan_vrf_tunnel_map(dvs, vrfname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", "0"), - ] - - # remove the VXLAN VRF tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VRF", '|', vrfname, - attrs, - ) - -def create_evpn_remote_vni(dvs, vlan_id, remotevtep, vnid): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remotevtep), - [ - ("vni", vnid), - ], - ) - time.sleep(2) - -def remove_vxlan_tunnel(dvs, tnlname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL", tnlname, - ) - -def remove_vxlan_tunnel_map(dvs, tnlname, mapname,vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - ) - -def remove_evpn_remote_vni(dvs, vlan_id, remotevtep ): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - ) - time.sleep(2) - -def create_vlan1(dvs, vlan_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - -def get_switch_mac(dvs): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH') - - entries = tbl.getKeys() - mac = None - for entry in entries: - status, fvs = tbl.get(entry) - assert status, "Got an error when get a key" - for key, value in fvs: - if key == 'SAI_SWITCH_ATTR_SRC_MAC_ADDRESS': - mac = value - break - else: - assert False, 'Don\'t found switch mac' - - return mac - -loopback_id = 0 -def_vr_id = 0 -switch_mac = None - - -class VxlanTunnel(object): - - ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" - ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" - ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" - ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" - ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" - ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" - ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" - ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" - ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" - ASIC_NEXT_HOP_GRP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" - ASIC_NEXT_HOP_GRP_MEMBERS = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" - - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_map_vrf_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - bridgeport_ids = set() - tunnel_map_map = {} - tunnel = {} - tunnelterm = {} - mapentry_map = {} - diptunnel_map = {} - diptunterm_map = {} - diptunstate_map = {} - bridgeport_map = {} - vlan_id_map = {} - vlan_member_map = {} - vr_map = {} - vnet_vr_ids = set() - nh_ids = {} - nh_grp_id = set() - nh_grp_member_id = set() - route_id = {} - - def fetch_exist_entries(self, dvs): - self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) - self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) - self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) - self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) - self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) - self.vnet_vr_ids = get_exist_entries(dvs, self.ASIC_VRF_TABLE) - self.rifs = get_exist_entries(dvs, self.ASIC_RIF_TABLE) - self.routes = get_exist_entries(dvs, self.ASIC_ROUTE_ENTRY) - self.nhops = get_exist_entries(dvs, self.ASIC_NEXT_HOP) - self.nhop_grp = get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP) - self.nhop_grp_members = get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP_MEMBERS) - - global switch_mac - - if switch_mac is None: - switch_mac = get_switch_mac(dvs) - - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - for x in range(1): - status, fvs = tbl.get(self.mapentry_map[tunnel_name + vidlist[x]]) - assert status == False, "SIP Tunnel Map entry not deleted" - - def check_vxlan_tunnel_vlan_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(1): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) - assert len(ret) > 0, "SIP TunnelMap entry not created" - assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" - self.mapentry_map[tunnel_name + vidlist[x]] = ret[0] - - - def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) - status, fvs = tbl.get(self.tunnelterm[tunnel_name]) - assert status == False, "SIP Tunnel Term entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.tunnel[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) - assert status == False, "SIP Tunnel mapper0 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) - assert status == False, "SIP Tunnel mapper1 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) - assert status == False, "SIP Tunnel mapper2 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) - assert status == False, "SIP Tunnel mapper3 not deleted" - - def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) - tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - } - ) - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, - { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - } - ) - - expected_attributes = { - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, - } - - check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(1): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) - - self.tunnel_map_ids.update(tunnel_map_id) - self.tunnel_ids.add(tunnel_id) - self.tunnel_term_ids.add(tunnel_term_id) - self.tunnel_map_map[tunnel_name] = tunnel_map_id - self.tunnel[tunnel_name] = tunnel_id - self.tunnelterm[tunnel_name] = tunnel_term_id - - def check_vxlan_dip_tunnel_delete(self, dvs, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') - status, fvs = tbl.get(self.diptunstate_map[dip]) - assert status == False, "State Table entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.diptunnel_map[dip]) - assert status == False, "Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) - status, fvs = tbl.get(self.bridgeport_map[dip]) - assert status == False, "Tunnel bridgeport entry not deleted" - - def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - expected_state_attributes = { - 'src_ip': src_ip, - 'dst_ip': dip, - 'tnl_src': 'EVPN', - } - - ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) - assert len(ret) > 0, "Tunnel Statetable entry not created" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.diptunstate_map[dip] = ret[0] - - tunnel_map_id = self.tunnel_map_map[vtep_name] - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - expected_tun_attributes = { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, - } - - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) - assert len(ret) > 0, "Tunnel entry not created" - assert len(ret) == 1, "More than 1 tunnel entry created" - - self.diptunnel_map[dip] = ret[0] - tunnel_id = ret[0] - - expected_bridgeport_attributes = { - 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', - 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, - 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', - 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', - } - - ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) - assert len(ret) > 0, "Bridgeport entry not created" - assert len(ret) == 1, "More than 1 bridgeport entry created" - - self.bridgeport_map[dip] = ret[0] - - def check_vlan_extension_delete(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') - status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) - assert status == False, "VLAN Member entry not deleted" - - def check_vlan_extension(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - expected_vlan_attributes = { - 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) - assert len(ret) > 0, "VLAN entry not created" - assert len(ret) == 1, "More than 1 VLAN entry created" - - self.vlan_id_map[vlan_name] = ret[0] - - expected_vlan_member_attributes = { - 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], - 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) - assert len(ret) > 0, "VLAN Member not created" - assert len(ret) == 1, "More than 1 VLAN member created" - self.vlan_member_map[dip+vlan_name] = ret[0] - - def check_vxlan_tunnel_vrf_map_entry(self, dvs, tunnel_name, vrf_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - if (self.tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - else: - tunnel_map_id = self.tunnel_map_map[tunnel_name] - - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created too early" - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[3], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vrf_name].get('ing'), - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, - } - ) - - self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[1]) - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[2], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[2], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vrf_name].get('egr'), - } - ) - - self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[2]) - self.tunnel_map_entry_ids.update(tunnel_map_entry_id) - - def check_vxlan_tunnel_vrf_map_entry_remove(self, dvs, tunnel_name, vrf_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tunnel_map_entry_id = get_deleted_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0]) - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1]) - for vrf_map_id in self.tunnel_map_vrf_entry_ids: - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, vrf_map_id) - - def check_router_interface(self, dvs, name, vlan_oid, route_count): - # Check RIF in ingress VRF - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - global switch_mac - - expected_attr = { - "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": self.vr_map[name].get('ing'), - "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": switch_mac, - } - - if vlan_oid: - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) - else: - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) - - new_rif = get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) - check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) - - #IP2ME route will be created with every router interface - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, route_count) - - self.rifs.add(new_rif) - self.routes.update(new_route) - - def check_del_router_interface(self, dvs, name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - old_rif = get_deleted_entries(asic_db, self.ASIC_RIF_TABLE, self.rifs, 1) - check_deleted_object(asic_db, self.ASIC_RIF_TABLE, old_rif[0]) - - self.rifs.remove(old_rif[0]) - - def vrf_route_ids(self, dvs, vrf_name): - vr_set = set() - - vr_set.add(self.vr_map[vrf_name].get('ing')) - return vr_set - - def check_vrf_routes(self, dvs, prefix, vrf_name, endpoint, tunnel, mac="", vni=0, no_update=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - vr_ids = self.vrf_route_ids(dvs, vrf_name) - count = len(vr_ids) - - # Check routes in ingress VRF - expected_attr = { - "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", - "SAI_NEXT_HOP_ATTR_IP": endpoint, - "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], - } - - if vni: - expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_VNI': vni}) - - if mac: - expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_MAC': mac}) - - if endpoint in self.nh_ids: - new_nh = self.nh_ids[endpoint] - else: - new_nh = get_created_entry(asic_db, self.ASIC_NEXT_HOP, self.nhops) - self.nh_ids[endpoint] = new_nh - self.nhops.add(new_nh) - - check_object(asic_db, self.ASIC_NEXT_HOP, new_nh, expected_attr) - if no_update: - count = 0 - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) - - if count: - self.route_id[vrf_name + ":" + prefix] = new_route - - #Check if the route is in expected VRF - asic_vrs = set() - for idx in range(count): - check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], - { - "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nh, - } - ) - rt_key = json.loads(new_route[idx]) - asic_vrs.add(rt_key['vr']) - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - if count: - assert asic_vrs == vr_ids - - self.routes.update(new_route) - - return True - - def check_vrf_routes_ecmp(self, dvs, prefix, vrf_name, tunnel, nh_count, no_update=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - vr_ids = self.vrf_route_ids(dvs, vrf_name) - count = len(vr_ids) - - new_nhg = get_created_entry(asic_db, self.ASIC_NEXT_HOP_GRP, self.nhop_grp) - self.nh_grp_id.add(new_nhg) - - if no_update: - count = 0 - - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) - if count: - self.route_id[vrf_name + ":" + prefix] = new_route - - #Check if the route is in expected VRF - asic_vrs = set() - for idx in range(count): - check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], - { - "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nhg, - } - ) - rt_key = json.loads(new_route[idx]) - asic_vrs.add(rt_key['vr']) - - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - if count: - assert asic_vrs == vr_ids - - self.routes.update(new_route) - - new_nhg_members = get_created_entries(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, self.nhop_grp_members, nh_count) - - for idx in range(nh_count): - self.nh_grp_member_id.add(new_nhg_members[idx]) - check_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, new_nhg_members[idx], - { - "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": new_nhg, - } - ) - - nhid_list = list() - - nhg_member_tbl = swsscommon.Table(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS) - - for k in new_nhg_members: - (status, fvs) = nhg_member_tbl.get(k) - assert status - - for v in fvs: - if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": - nhid = v[1] - nhid_list.append(nhid) - - return nhid_list - - def check_add_tunnel_nexthop(self, dvs, nh_id, endpoint, tunnel, mac, vni): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - # Check routes in ingress VRF - expected_attr = { - "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", - "SAI_NEXT_HOP_ATTR_IP": endpoint, - "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], - "SAI_NEXT_HOP_ATTR_TUNNEL_VNI": vni, - "SAI_NEXT_HOP_ATTR_TUNNEL_MAC": mac, - } - - check_object(asic_db, self.ASIC_NEXT_HOP, nh_id, expected_attr) - - def check_del_tunnel_nexthop(self, dvs, vrf_name, endpoint, tunnel, mac="", vni=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - del_nh_ids = get_deleted_entries(asic_db, self.ASIC_NEXT_HOP, self.nhops, 1) - check_deleted_object(asic_db, self.ASIC_NEXT_HOP, del_nh_ids[0]) - check_deleted_object(asic_db, self.ASIC_NEXT_HOP, self.nh_ids[endpoint]) - assert del_nh_ids[0] == self.nh_ids[endpoint] - self.nh_ids.pop(endpoint) - return True - - def check_vrf_routes_ecmp_nexthop_grp_del(self, dvs, nh_count): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - nh_grp_id_len = len(self.nh_grp_id) - assert nh_grp_id_len == 1 - - for nh_grp_id in self.nh_grp_id: - check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP, nh_grp_id) - self.nh_grp_id.clear() - - nh_grp_member_id_len = len(self.nh_grp_member_id) - assert nh_grp_member_id_len == nh_count - - for nh_grp_member_id in self.nh_grp_member_id: - check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, nh_grp_member_id) - - self.nh_grp_member_id.clear() - - def check_del_vrf_routes(self, dvs, prefix, vrf_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - del_route = get_deleted_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) - - for idx in range(1): - rt_key = json.loads(del_route[idx]) - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - check_deleted_object(asic_db, self.ASIC_ROUTE_ENTRY, self.route_id[vrf_name + ":" + prefix]) - self.route_id.clear() - - return True - - def create_vrf(self, dvs, vrf_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - initial_entries = set(tbl.getKeys()) - - attrs = [ - ("vni", "0"), - ] - tbl = swsscommon.Table(conf_db, "VRF") - fvs = swsscommon.FieldValuePairs(attrs) - tbl.set(vrf_name, fvs) - time.sleep(2) - - tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - current_entries = set(tbl.getKeys()) - assert len(current_entries - initial_entries) == 1 - - new_vr_ids = get_created_entries(asic_db, self.ASIC_VRF_TABLE, self.vnet_vr_ids, 1) - self.vnet_vr_ids.update(new_vr_ids) - self.vr_map[vrf_name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[0]} - - return list(current_entries - initial_entries)[0] - - def remove_vrf(self, dvs, vrf_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VRF") - tbl._del(vrf_name) - time.sleep(2) - - - def is_vrf_attributes_correct(self, db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = set(tbl.getKeys()) - assert key in keys, "The created key wasn't found" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - # filter the fake 'NULL' attribute out - fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) - - attr_keys = {entry[0] for entry in fvs} - assert attr_keys == set(expected_attributes.keys()) - - for name, value in fvs: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper class TestL3Vxlan(object): def get_vxlan_obj(self): return VxlanTunnel() + def get_vxlan_helper(self): + return VxlanEvpnHelper() + def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -1015,6 +24,7 @@ def setup_db(self, dvs): # @pytest.mark.dev_sanity def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1025,15 +35,15 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): print ("\n\nTesting Create and Delete SIP Tunnel and VRF VNI Map entries") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') print ("\tTesting VRF-VNI map in APP DB") vlanlist = ['100'] @@ -1046,7 +56,7 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1056,31 +66,31 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan VNI Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel VRF VNI Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Tunnel VRF VNI Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.remove_vrf(dvs, "Vrf-RED") vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Tunnel Vlan VNI Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) - remove_vlan(dvs, "100") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vlan(dvs, "100") # Test 2 - Create and Delete DIP Tunnel on adding and removing prefix route @@ -1088,6 +98,7 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): # @pytest.mark.dev_sanity def test_prefix_route_create_dip_tunnel(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1097,17 +108,17 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): print ("\n\nTesting Create and Delete DIP Tunnel on adding and removing prefix route") print ("\tCreate SIP Tunnel") - vlan_ids = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - vlan_oid = create_vlan(dvs,"Vlan100", vlan_ids) - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vlan_ids = helper.get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_oid = vxlan_obj.create_vlan(dvs,"Vlan100", vlan_ids) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') print ("\tTesting VRF-VNI map in APP DB") vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') vlanlist = ['100'] vnilist = ['1000'] @@ -1119,7 +130,7 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1129,31 +140,31 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vlan_oid, 2) print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTesting DIP tunnel 7.7.7.7 creation") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1161,24 +172,24 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting Tunnel Vrf Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Test 3 - Create and Delete DIP Tunnel and Test IPv4 route and overlay nexthop add and delete @@ -1186,6 +197,7 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): # @pytest.mark.dev_sanity def test_dip_tunnel_ipv4_routes(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1195,15 +207,15 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTesting IPv4 Route and Overlay Nexthop Add and Delete") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') print ("\tTesting VRF-VNI map in APP DB") vlanlist = ['100'] @@ -1216,7 +228,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1226,26 +238,26 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting First DIP tunnel creation to 7.7.7.7") - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTesting VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting Second DIP tunnel creation to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -1253,27 +265,27 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -1281,7 +293,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1296,7 +308,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) @@ -1306,9 +318,9 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1316,7 +328,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') ecmp_nexthop_attr = [ @@ -1327,7 +339,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") - create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -1337,7 +349,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -1348,41 +360,41 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTest DIP and SIP Tunnel Deletion ") print ("\tTesting Tunnel Vrf VNI Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting LastVlan removal and DIP tunnel delete for 7.7.7.7") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting LastVlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Test 4 - Create and Delete DIP Tunnel and Test IPv6 route and overlay nexthop add and delete @@ -1390,6 +402,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): # @pytest.mark.dev_sanity def test_dip_tunnel_ipv6_routes(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1399,16 +412,16 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTesting IPv6 Route and Overlay Nexthop Add and Delete") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') print ("\tTesting VRF-VNI map in APP DB") vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') vlanlist = ['100'] vnilist = ['1000'] @@ -1421,7 +434,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] print ("\tCheck VRF Table in APP DB") - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1431,26 +444,26 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting First DIP tunnel creation to 7.7.7.7") - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTesting VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting Second DIP tunnel creation to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -1459,27 +472,27 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -1487,7 +500,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -1502,7 +515,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ] print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) @@ -1512,9 +525,9 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -1522,7 +535,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") @@ -1533,7 +546,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), ] - create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -1543,7 +556,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -1554,41 +567,41 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTest DIP and SIP Tunnel Deletion ") print ("\tTesting Tunnel Vrf Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting LastVlan removal and DIP tunnel delete for 7.7.7.7") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting LastVlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "2001::8/64") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "2001::8/64") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Add Dummy always-pass test at end as workaroud diff --git a/tests/test_evpn_l3_vxlan_p2mp.py b/tests/test_evpn_l3_vxlan_p2mp.py new file mode 100644 index 0000000000..cee794aedd --- /dev/null +++ b/tests/test_evpn_l3_vxlan_p2mp.py @@ -0,0 +1,596 @@ +from swsscommon import swsscommon +import time +import json +import random +import pytest +from pprint import pprint +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper + +DVS_FAKE_PLATFORM = "mellanox" + +class TestL3VxlanP2MP(object): + + def get_vxlan_obj(self): + return VxlanTunnel() + + def get_vxlan_helper(self): + return VxlanEvpnHelper() + + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + +# Test 1 - Create and Delete SIP Tunnel and VRF VNI Map entries +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting Create and Delete SIP Tunnel and VRF VNI Map entries") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + print ("\tTesting VRF-VNI map in APP DB") + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan VNI Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel VRF VNI Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Tunnel VRF VNI Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Tunnel Vlan VNI Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 2 - Remote end point add +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_prefix_route_create_remote_endpoint(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\tCreate SIP Tunnel") + vlan_ids = vxlan_obj.helper.get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_oid = vxlan_obj.create_vlan(dvs,"Vlan100", vlan_ids) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + print ("\tTesting VRF-VNI map in APP DB") + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vlan_oid, 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\tTesting Tunnel Vrf Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 3 - Create and Delete remote end point and Test IPv4 route and overlay nexthop add and delete +# @pytest.mark.dev_sanity + def test_remote_ipv4_routes(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting IPv4 Route and Overlay Nexthop Add and Delete") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + print ("\tTesting VRF-VNI map in APP DB") + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting First Remote end point to 7.7.7.7") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print ("\tTesting VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting Second remote end point to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + + print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest VRF IPv4 Route with ECMP Tunnel Nexthop Add and Delete") + vxlan_obj.fetch_exist_entries(dvs) + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") + vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") + vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") + vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop ECMP Group is deleted") + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest Remote end point remove and SIP Tunnel Deletion ") + print ("\tTesting Tunnel Vrf VNI Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting LastVlan removal and remote end point delete for 7.7.7.7") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting LastVlan removal and remote end point delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 4 - Create and Delete remote endpoint and Test IPv6 route and overlay nexthop add and delete +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_remote_ipv6_routes(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting IPv6 Route and Overlay Nexthop Add and Delete") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + print ("\tTesting VRF-VNI map in APP DB") + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + print ("\tCheck VRF Table in APP DB") + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting First remote endpoint creation to 7.7.7.7") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print ("\tTesting VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting Second remote endpoint creation to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + + print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + vxlan_obj.fetch_exist_entries(dvs) + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest VRF IPv6 Route with ECMP Tunnel Nexthop Add and delete") + vxlan_obj.fetch_exist_entries(dvs) + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") + vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") + vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop ECMP Group is deleted") + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest remote endpoint and SIP Tunnel Deletion ") + print ("\tTesting Tunnel Vrf Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting LastVlan removal and remote endpoint delete for 7.7.7.7") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting LastVlan removal and remote endpoint delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "2001::8/64") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass diff --git a/tests/test_evpn_p2p_tunnel.py b/tests/test_evpn_p2p_tunnel.py new file mode 100644 index 0000000000..c8a150595f --- /dev/null +++ b/tests/test_evpn_p2p_tunnel.py @@ -0,0 +1,690 @@ +from swsscommon import swsscommon +import time +import json +import random +import pytest +from pprint import pprint + + +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) + +def delete_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + +def create_entry_pst(db, table, separator, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def entries(db, table): + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + +def get_exist_entries(dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def get_created_entry(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + +def get_created_entries(db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + new_entries.sort() + return new_entries + +def get_default_vr_id(dvs): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert len(keys) == 1, "Wrong number of virtual routers found" + + return keys[0] + + +def check_object(db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + if name in expected_attributes: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + +def get_key_with_attr(db, table, expected_attributes ): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + retkey = list() + #assert key in keys, "The desired key is not presented" + + for key in keys: + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + num_match = 0 + for name, value in fvs: + if name in expected_attributes: + if expected_attributes[name] == value: + num_match += 1 + if num_match == len(expected_attributes): + retkey.append(key) + + return retkey + + +def create_evpn_nvo(dvs, nvoname, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("source_vtep", tnl_name), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VXLAN_EVPN_NVO", '|', nvoname, + attrs, + ) + +def remove_evpn_nvo(dvs, nvoname): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) + +def create_vxlan_tunnel(dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("src_ip", src_ip), + ] + + if not skip_dst_ip: + attrs.append(("dst_ip", dst_ip)) + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VXLAN_TUNNEL", '|', name, + attrs, + ) + +def create_vxlan_tunnel_map(dvs, tnl_name, map_name, vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + create_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnl_name, map_name), + attrs, + ) + +def create_evpn_remote_vni(dvs, vlan_id, remote_vtep, vnid): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + create_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remote_vtep), + [ + ("vni", vnid), + ], + ) + time.sleep(2) + +def remove_vxlan_tunnel(dvs, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create the VXLAN tunnel Term entry in Config DB + delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL", tnl_name, + ) + +def remove_vxlan_tunnel_map(dvs, tnl_name, map_name,vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), + ) + +def remove_evpn_remote_vni(dvs, vlan_id, remote_vtep ): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + delete_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), + ) + time.sleep(2) + +def create_vlan1(dvs, vlan_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + create_entry_tbl( + conf_db, + "VLAN", '|', vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + +loopback_id = 0 +def_vr_id = 0 +switch_mac = None + +class VxlanTunnel(object): + + ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" + ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" + ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_ids = set() + tunnel_term_ids = set() + bridgeport_ids = set() + tunnel_map_map = {} + tunnel = {} + tunnel_appdb = {} + tunnel_term = {} + map_entry_map = {} + dip_tunnel_map = {} + dip_tun_state_map = {} + bridgeport_map = {} + vlan_id_map = {} + vlan_member_map = {} + + def fetch_exist_entries(self, dvs): + self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) + self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) + self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) + self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) + self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) + + def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + for x in range(len(vidlist)): + status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) + assert status == False, "SIP Tunnel Map entry not deleted" + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode != 0, "Kernel device not deleted" + + def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) + assert len(ret) > 0, "SIP TunnelMap entry not created" + assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" + self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode == 0, "Kernel device not created" + + + def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") + status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from APP_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) + status, fvs = tbl.get(self.tunnel_term[tunnel_name]) + assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.tunnel[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) + assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) + assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) + assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) + assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" + + def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) + tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) + tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) + + # check that the vxlan tunnel termination are there + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" + + check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + } + ) + + check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + } + ) + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + + check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + } + ) + + expected_attributes = { + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, + } + + if not skip_dst_ip: + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' + + check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) + + expected_siptnl_attributes = { + 'src_ip': src_ip, + } + + if not skip_dst_ip: + expected_siptnl_attributes['dst_ip'] = dst_ip + + ret = get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) + assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.tunnel_appdb[tunnel_name] = ret[0] + + self.tunnel_map_ids.update(tunnel_map_id) + self.tunnel_ids.add(tunnel_id) + self.tunnel_term_ids.add(tunnel_term_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_id + self.tunnel[tunnel_name] = tunnel_id + self.tunnel_term[tunnel_name] = tunnel_term_id + + def check_vxlan_dip_tunnel_delete(self, dvs, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') + status, fvs = tbl.get(self.dip_tun_state_map[dip]) + assert status == False, "State Table entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.dip_tunnel_map[dip]) + assert status == False, "Tunnel entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) + status, fvs = tbl.get(self.bridgeport_map[dip]) + assert status == False, "Tunnel bridgeport entry not deleted" + + def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + expected_state_attributes = { + 'src_ip': src_ip, + 'dst_ip': dip, + 'tnl_src': 'EVPN', + } + + ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) + assert len(ret) > 0, "Tunnel Statetable entry not created" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.dip_tun_state_map[dip] = ret[0] + + + tunnel_map_id = self.tunnel_map_map[vtep_name] + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + + print(decapstr) + print(encapstr) + + expected_tun_attributes = { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, + } + + ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) + assert len(ret) > 0, "Tunnel entry not created" + assert len(ret) == 1, "More than 1 tunnel entry created" + + self.dip_tunnel_map[dip] = ret[0] + tunnel_id = ret[0] + + expected_bridgeport_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + + ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) + assert len(ret) > 0, "Bridgeport entry not created" + assert len(ret) == 1, "More than 1 bridgeport entry created" + + self.bridgeport_map[dip] = ret[0] + + def check_vlan_extension_delete(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') + status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) + assert status == False, "VLAN Member entry not deleted" + + def check_vlan_extension(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + expected_vlan_attributes = { + 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, + } + ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) + assert len(ret) > 0, "VLAN entry not created" + assert len(ret) == 1, "More than 1 VLAN entry created" + + self.vlan_id_map[vlan_name] = ret[0] + + expected_vlan_member_attributes = { + 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], + 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], + } + ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) + assert len(ret) > 0, "VLAN Member not created" + assert len(ret) == 1, "More than 1 VLAN member created" + self.vlan_member_map[dip+vlan_name] = ret[0] + + def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + time.sleep(2) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + + # check that the vxlan tunnel termination are there + assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" + + check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + +class TestVxlanOrch(object): + + def get_vxlan_obj(self): + return VxlanTunnel() + +# Test 1 - Create and Delete SIP Tunnel and Map entries + def test_p2mp_tunnel(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_1' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + + vxlan_obj.fetch_exist_entries(dvs) + + create_vlan1(dvs,"Vlan100") + create_vlan1(dvs,"Vlan101") + create_vlan1(dvs,"Vlan102") + create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + print("Testing SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print("Testing Tunnel Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing Tunnel Map entry removal") + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + +# Test 2 - DIP Tunnel Tests + def test_p2p_tunnel(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + vxlan_obj.fetch_exist_entries(dvs) + create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + create_evpn_nvo(dvs, 'nvo1', tunnel_name) + create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print("Testing DIP tunnel creation") + vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') + print("Testing VLAN 100 extension") + vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') + + create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') + create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') + + print("Testing DIP tunnel not created again") + vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') + + print("Testing VLAN 101 extension") + vxlan_obj.check_vlan_extension(dvs, '101', '7.7.7.7') + + print("Testing VLAN 102 extension") + vxlan_obj.check_vlan_extension(dvs, '102', '7.7.7.7') + + print("Testing another DIP tunnel to 8.8.8.8") + create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + print("Testing DIP tunnel creation to 8.8.8.8") + vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') + print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension(dvs, '100', '8.8.8.8') + vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') + + print("Testing Vlan Extension removal") + remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete(dvs, '101', '7.7.7.7') + print("Testing DIP tunnel not deleted") + vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') + + print("Testing Last Vlan removal and DIP tunnel delete") + remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete(dvs, '102', '7.7.7.7') + vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') + + print("Testing Last Vlan removal and DIP tunnel delete for 8.8.8.8") + remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') + vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') + + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + remove_evpn_nvo(dvs, 'nvo1') + remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + + +# Test 3 - Create and Delete SIP Tunnel and Map entries + def test_p2mp_tunnel_with_dip(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + + vxlan_obj.fetch_exist_entries(dvs) + + create_vlan1(dvs,"Vlan100") + create_vlan1(dvs,"Vlan101") + create_vlan1(dvs,"Vlan102") + create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) + create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + print("Testing SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist, '2.2.2.2', False) + + print("Testing Tunnel Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing Tunnel Map entry removal") + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) diff --git a/tests/test_evpn_tunnel.py b/tests/test_evpn_tunnel.py index c8a150595f..69918f4b1e 100644 --- a/tests/test_evpn_tunnel.py +++ b/tests/test_evpn_tunnel.py @@ -4,532 +4,9 @@ import random import pytest from pprint import pprint +from evpn_tunnel import VxlanTunnel - -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) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_entry_pst(db, table, separator, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - - -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - -def get_exist_entries(dvs, table): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_created_entry(db, table, existed_entries): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == 1, "Wrong number of created entries." - return new_entries[0] - -def get_created_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." - new_entries.sort() - return new_entries - -def get_default_vr_id(dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] - - -def check_object(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key in keys, "The desired key is not presented" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - for name, value in fvs: - if name in expected_attributes: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - -def get_key_with_attr(db, table, expected_attributes ): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - retkey = list() - #assert key in keys, "The desired key is not presented" - - for key in keys: - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - num_match = 0 - for name, value in fvs: - if name in expected_attributes: - if expected_attributes[name] == value: - num_match += 1 - if num_match == len(expected_attributes): - retkey.append(key) - - return retkey - - -def create_evpn_nvo(dvs, nvoname, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("source_vtep", tnl_name), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_EVPN_NVO", '|', nvoname, - attrs, - ) - -def remove_evpn_nvo(dvs, nvoname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - if not skip_dst_ip: - attrs.append(("dst_ip", dst_ip)) - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - -def create_vxlan_tunnel_map(dvs, tnl_name, map_name, vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnl_name, map_name), - attrs, - ) - -def create_evpn_remote_vni(dvs, vlan_id, remote_vtep, vnid): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remote_vtep), - [ - ("vni", vnid), - ], - ) - time.sleep(2) - -def remove_vxlan_tunnel(dvs, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL", tnl_name, - ) - -def remove_vxlan_tunnel_map(dvs, tnl_name, map_name,vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), - ) - -def remove_evpn_remote_vni(dvs, vlan_id, remote_vtep ): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), - ) - time.sleep(2) - -def create_vlan1(dvs, vlan_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - -loopback_id = 0 -def_vr_id = 0 -switch_mac = None - -class VxlanTunnel(object): - - ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" - ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" - ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" - ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" - ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" - - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - bridgeport_ids = set() - tunnel_map_map = {} - tunnel = {} - tunnel_appdb = {} - tunnel_term = {} - map_entry_map = {} - dip_tunnel_map = {} - dip_tun_state_map = {} - bridgeport_map = {} - vlan_id_map = {} - vlan_member_map = {} - - def fetch_exist_entries(self, dvs): - self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) - self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) - self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) - self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) - self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) - - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - for x in range(len(vidlist)): - status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) - assert status == False, "SIP Tunnel Map entry not deleted" - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode != 0, "Kernel device not deleted" - - def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) - assert len(ret) > 0, "SIP TunnelMap entry not created" - assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" - self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode == 0, "Kernel device not created" - - - def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") - status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from APP_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) - status, fvs = tbl.get(self.tunnel_term[tunnel_name]) - assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.tunnel[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) - assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) - assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) - assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) - assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" - - def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) - tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - } - ) - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, - - check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, - { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - } - ) - - expected_attributes = { - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, - } - - if not skip_dst_ip: - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' - - check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) - - expected_siptnl_attributes = { - 'src_ip': src_ip, - } - - if not skip_dst_ip: - expected_siptnl_attributes['dst_ip'] = dst_ip - - ret = get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) - assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.tunnel_appdb[tunnel_name] = ret[0] - - self.tunnel_map_ids.update(tunnel_map_id) - self.tunnel_ids.add(tunnel_id) - self.tunnel_term_ids.add(tunnel_term_id) - self.tunnel_map_map[tunnel_name] = tunnel_map_id - self.tunnel[tunnel_name] = tunnel_id - self.tunnel_term[tunnel_name] = tunnel_term_id - - def check_vxlan_dip_tunnel_delete(self, dvs, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') - status, fvs = tbl.get(self.dip_tun_state_map[dip]) - assert status == False, "State Table entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.dip_tunnel_map[dip]) - assert status == False, "Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) - status, fvs = tbl.get(self.bridgeport_map[dip]) - assert status == False, "Tunnel bridgeport entry not deleted" - - def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - expected_state_attributes = { - 'src_ip': src_ip, - 'dst_ip': dip, - 'tnl_src': 'EVPN', - } - - ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) - assert len(ret) > 0, "Tunnel Statetable entry not created" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.dip_tun_state_map[dip] = ret[0] - - - tunnel_map_id = self.tunnel_map_map[vtep_name] - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - print(decapstr) - print(encapstr) - - expected_tun_attributes = { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, - } - - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) - assert len(ret) > 0, "Tunnel entry not created" - assert len(ret) == 1, "More than 1 tunnel entry created" - - self.dip_tunnel_map[dip] = ret[0] - tunnel_id = ret[0] - - expected_bridgeport_attributes = { - 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', - 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, - 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', - 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', - } - - ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) - assert len(ret) > 0, "Bridgeport entry not created" - assert len(ret) == 1, "More than 1 bridgeport entry created" - - self.bridgeport_map[dip] = ret[0] - - def check_vlan_extension_delete(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') - status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) - assert status == False, "VLAN Member entry not deleted" - - def check_vlan_extension(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - expected_vlan_attributes = { - 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) - assert len(ret) > 0, "VLAN entry not created" - assert len(ret) == 1, "More than 1 VLAN entry created" - - self.vlan_id_map[vlan_name] = ret[0] - - expected_vlan_member_attributes = { - 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], - 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) - assert len(ret) > 0, "VLAN Member not created" - assert len(ret) == 1, "More than 1 VLAN member created" - self.vlan_member_map[dip+vlan_name] = ret[0] - - def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - time.sleep(2) - - if (self.tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) - else: - tunnel_map_id = self.tunnel_map_map[tunnel_name] - - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), - } - ) - - self.tunnel_map_entry_ids.update(tunnel_map_entry_id) +DVS_FAKE_PLATFORM = "broadcom" class TestVxlanOrch(object): @@ -547,13 +24,13 @@ def test_p2mp_tunnel(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vlanlist = ['100', '101', '102'] vnilist = ['1000', '1001', '1002'] @@ -565,14 +42,14 @@ def test_p2mp_tunnel(self, dvs, testlog): vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') # Test 2 - DIP Tunnel Tests def test_p2p_tunnel(self, dvs, testlog): @@ -586,24 +63,24 @@ def test_p2p_tunnel(self, dvs, testlog): vnilist = ['1000', '1001', '1002'] vxlan_obj.fetch_exist_entries(dvs) - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) - create_evpn_nvo(dvs, 'nvo1', tunnel_name) - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') print("Testing DIP tunnel creation") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print("Testing VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') - create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') - create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') print("Testing DIP tunnel not created again") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') @@ -615,7 +92,7 @@ def test_p2p_tunnel(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '102', '7.7.7.7') print("Testing another DIP tunnel to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') print("Testing DIP tunnel creation to 8.8.8.8") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -623,32 +100,32 @@ def test_p2p_tunnel(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print("Testing Vlan Extension removal") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') - remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '101', '7.7.7.7') print("Testing DIP tunnel not deleted") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print("Testing Last Vlan removal and DIP tunnel delete") - remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '102', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print("Testing Last Vlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_evpn_nvo(dvs, 'nvo1') - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') # Test 3 - Create and Delete SIP Tunnel and Map entries @@ -662,13 +139,13 @@ def test_p2mp_tunnel_with_dip(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vlanlist = ['100', '101', '102'] vnilist = ['1000', '1001', '1002'] @@ -680,11 +157,11 @@ def test_p2mp_tunnel_with_dip(self, dvs, testlog): vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') diff --git a/tests/test_evpn_tunnel_p2mp.py b/tests/test_evpn_tunnel_p2mp.py new file mode 100644 index 0000000000..a446c22384 --- /dev/null +++ b/tests/test_evpn_tunnel_p2mp.py @@ -0,0 +1,119 @@ +from swsscommon import swsscommon +import time +import json +import random +import pytest +from pprint import pprint +from evpn_tunnel import VxlanTunnel + +DVS_FAKE_PLATFORM = "mellanox" + +class TestVxlanOrchP2MP(object): + + def get_vxlan_obj(self): + return VxlanTunnel() + +# Test 1 - Create and Delete SIP Tunnel and Map entries + def test_p2mp_tunnel(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_1' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + + vxlan_obj.fetch_exist_entries(dvs) + + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + print("Testing SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print("Testing Tunnel Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + +# Test 2 - Vlan extension Tests + def test_vlan_extension(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print("Testing VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') + + print("Testing VLAN 101 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '101', '6.6.6.6', '7.7.7.7') + + print("Testing VLAN 102 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '102', '6.6.6.6', '7.7.7.7') + + print("Testing another remote endpoint to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + print("Testing remote endpoint creation to 8.8.8.8") + + print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print("Testing Vlan Extension removal") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '101', '6.6.6.6', '7.7.7.7') + + print("Testing Last Vlan removal and remote endpoint delete") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '102', '6.6.6.6', '7.7.7.7') + + print("Testing Last Vlan removal and remote endpoint delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') From d594c47b9d7e8270a0b517834359ebca5799dfe4 Mon Sep 17 00:00:00 2001 From: Wenda Ni Date: Fri, 2 Jul 2021 17:52:46 -0700 Subject: [PATCH 02/28] [acl mirror action] Mirror session ref count fix at acl rule attachment (#1761) * Fix mirror session ref count at acl rule attachement Signed-off-by: wenda.ni --- orchagent/aclorch.cpp | 12 +++++------- tests/test_mirror.py | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 01b6a8e0bc..7f2ccfb50c 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -1225,13 +1225,6 @@ bool AclRuleMirror::create() SWSS_LOG_THROW("Failed to get mirror session state for session %s", m_sessionName.c_str()); } - // Increase session reference count regardless of state to deny - // attempt to remove mirror session with attached ACL rules. - if (!m_pMirrorOrch->increaseRefCount(m_sessionName)) - { - SWSS_LOG_THROW("Failed to increase mirror session reference count for session %s", m_sessionName.c_str()); - } - if (!state) { return true; @@ -1254,6 +1247,11 @@ bool AclRuleMirror::create() return false; } + if (!m_pMirrorOrch->increaseRefCount(m_sessionName)) + { + SWSS_LOG_THROW("Failed to increase mirror session reference count for session %s", m_sessionName.c_str()); + } + m_state = true; return true; diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 73cfc02ccf..f74ff6fa0d 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -805,12 +805,11 @@ def test_AclBindMirrorPerStage(self, dvs, testlog): self.remove_ip_address("Ethernet32", "20.0.0.0/31") self.set_interface_status(dvs, "Ethernet32", "down") - def test_AclBindMirror(self, dvs, testlog): + def _test_AclBindMirror(self, dvs, testlog, create_seq_test=False): """ This test tests ACL associated with mirror session with DSCP value The DSCP value is tested on both with mask and without mask """ - self.setup_db(dvs) session = "MIRROR_SESSION" acl_table = "MIRROR_TABLE" @@ -820,16 +819,18 @@ def test_AclBindMirror(self, dvs, testlog): self.set_interface_status(dvs, "Ethernet32", "up") self.add_ip_address("Ethernet32", "20.0.0.0/31") self.add_neighbor("Ethernet32", "20.0.0.1", "02:04:06:08:10:12") - self.add_route(dvs, "4.4.4.4", "20.0.0.1") + if create_seq_test == False: + self.add_route(dvs, "4.4.4.4", "20.0.0.1") # create mirror session self.create_mirror_session(session, "3.3.3.3", "4.4.4.4", "0x6558", "8", "100", "0") - assert self.get_mirror_session_state(session)["status"] == "active" + assert self.get_mirror_session_state(session)["status"] == ("active" if create_seq_test == False else "inactive") - # assert mirror session in asic database + # check mirror session in asic database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") - assert len(tbl.getKeys()) == 1 - mirror_session_oid = tbl.getKeys()[0] + assert len(tbl.getKeys()) == (1 if create_seq_test == False else 0) + if create_seq_test == False: + mirror_session_oid = tbl.getKeys()[0] # create acl table self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"]) @@ -837,10 +838,25 @@ def test_AclBindMirror(self, dvs, testlog): # create acl rule with dscp value 48 self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "48", session) - # assert acl rule is created + # acl rule creation check tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] - assert len(rule_entries) == 1 + assert len(rule_entries) == (1 if create_seq_test == False else 0) + + if create_seq_test == True: + self.add_route(dvs, "4.4.4.4", "20.0.0.1") + + assert self.get_mirror_session_state(session)["status"] == "active" + + # assert mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + mirror_session_oid = tbl.getKeys()[0] + + # assert acl rule is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(rule_entries) == 1 (status, fvs) = tbl.get(rule_entries[0]) assert status == True @@ -888,6 +904,12 @@ def test_AclBindMirror(self, dvs, testlog): self.remove_ip_address("Ethernet32", "20.0.0.0/31") self.set_interface_status(dvs, "Ethernet32", "down") + def test_AclBindMirror(self, dvs, testlog): + self.setup_db(dvs) + + self._test_AclBindMirror(dvs, testlog) + self._test_AclBindMirror(dvs, testlog, create_seq_test=True) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying From 5528ebf1b8a4d1143f62523a79736474ae3acd70 Mon Sep 17 00:00:00 2001 From: Kamil Cudnik Date: Mon, 5 Jul 2021 17:48:08 +0200 Subject: [PATCH 03/28] Cleanup code (#1814) --- .gitignore | 8 ++++++++ orchagent/countercheckorch.cpp | 7 +++---- orchagent/main.cpp | 6 +++--- orchagent/mirrororch.cpp | 4 ++-- orchagent/natorch.cpp | 3 --- orchagent/pfcactionhandler.cpp | 4 ++-- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 439a0999f3..bfba272305 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,11 @@ stamp-h1 **/Makefile autom4te.cache +# Temp files # +############## +*~ +*.swp + # Dependency Folder # ##################### deps/ @@ -52,6 +57,8 @@ cfgmgr/vxlanmgrd cfgmgr/natmgrd cfgmgr/sflowmgrd cfgmgr/macsecmgrd +cfgmgr/coppmgrd +cfgmgr/tunnelmgrd fpmsyncd/fpmsyncd gearsyncd/gearsyncd mclagsyncd/mclagsyncd @@ -68,6 +75,7 @@ tlm_teamd/tlm_teamd teamsyncd/teamsyncd tests/tests + # Test Files # ############## tests/log diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp index 54d6c1a51c..1c4735888f 100644 --- a/orchagent/countercheckorch.cpp +++ b/orchagent/countercheckorch.cpp @@ -164,7 +164,7 @@ PfcFrameCounters CounterCheckOrch::getPfcFrameCounters(sai_object_id_t portId) if (!m_countersTable->get(sai_serialize_object_id(portId), fieldValues)) { - return move(counters); + return counters; } for (const auto& fv : fieldValues) @@ -182,7 +182,7 @@ PfcFrameCounters CounterCheckOrch::getPfcFrameCounters(sai_object_id_t portId) } } - return move(counters); + return counters; } QueueMcCounters CounterCheckOrch::getQueueMcCounters( @@ -218,10 +218,9 @@ QueueMcCounters CounterCheckOrch::getQueueMcCounters( counters.push_back(pkts); } - return move(counters); + return counters; } - void CounterCheckOrch::addPort(const Port& port) { m_mcCountersMap.emplace(port.m_port_id, getQueueMcCounters(port)); diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 018af5999b..10dd8cb788 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -85,11 +85,11 @@ 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 (deprecated, use -z)" << endl; - cout << " -z: redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async" << 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; - cout << " -k max bulk size in bulk mode (default 1000)"; + cout << " -k max bulk size in bulk mode (default 1000)" << endl; } void sighup_handler(int signo) diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index c2c78c7601..37c2ef73df 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -759,7 +759,7 @@ bool MirrorOrch::setUnsetPortMirror(Port port, status = sai_port_api->set_port_attribute(p.m_port_id, &port_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to configure %s session on port %s: %s, status %d, sessionId %x", + SWSS_LOG_ERROR("Failed to configure %s session on port %s: %s, status %d, sessionId %lx", ingress ? "RX" : "TX", port.m_alias.c_str(), p.m_alias.c_str(), status, sessionId); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); @@ -775,7 +775,7 @@ bool MirrorOrch::setUnsetPortMirror(Port port, status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to configure %s session on port %s, status %d, sessionId %x", + SWSS_LOG_ERROR("Failed to configure %s session on port %s, status %d, sessionId %lx", ingress ? "RX" : "TX", port.m_alias.c_str(), status, sessionId); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) diff --git a/orchagent/natorch.cpp b/orchagent/natorch.cpp index 283110efd1..d7f124a28e 100644 --- a/orchagent/natorch.cpp +++ b/orchagent/natorch.cpp @@ -2667,7 +2667,6 @@ void NatOrch::doNatTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_TABLE:65.55.45.1 * translated_ip: 10.0.0.1 @@ -2808,7 +2807,6 @@ void NatOrch::doTwiceNatTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_TWICE_TABLE:91.91.91.91:65.55.45.1 * translated_src_ip: 14.14.14.14 @@ -3020,7 +3018,6 @@ void NatOrch::doDnatPoolTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_DNAT_POOL_TABLE:65.55.45.1 * NULL: NULL diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 34c513e5d6..cf43f57d08 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -121,7 +121,7 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr if (!countersTable->get(queueIdStr, fieldValues)) { - return move(stats); + return stats; } for (const auto& fv : fieldValues) @@ -175,7 +175,7 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr } } - return move(stats); + return stats; } void PfcWdActionHandler::initWdCounters(shared_ptr countersTable, const string &queueIdStr) From 7c7c4513112177d780cf43697754ed3db51d7e1c Mon Sep 17 00:00:00 2001 From: Song Yuan <64041228+ysmanman@users.noreply.github.com> Date: Tue, 6 Jul 2021 08:14:25 -0700 Subject: [PATCH 04/28] Revert recirc port change (#1813) Recirc port support has been added to recent versions of DNX SAI. The earlier special handling for recirc port is not required anymore and thus reverted. --- orchagent/portsorch.cpp | 94 +++-------------------------------------- orchagent/portsorch.h | 1 - 2 files changed, 5 insertions(+), 90 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index a699ddded1..9b22a551b5 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2235,11 +2235,6 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde { SWSS_LOG_ENTER(); - if (role == "Rec" || role == "Inb") - { - return doProcessRecircPort(alias, role, lane_set, SET_COMMAND); - } - /* Determine if the lane combination exists in switch */ if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end()) { @@ -2294,6 +2289,11 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde m_portList[alias].m_init = true; + if (role == "Rec" || role == "Inb") + { + m_recircPortRole[alias] = role; + } + SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); } else @@ -2756,16 +2756,6 @@ void PortsOrch::doPortTask(Consumer &consumer) } else { - /* Skip configuring recirc port for now because the current SAI implementation of some vendors - * have limiited support for recirc port. This check can be removed once SAI implementation - * is enhanced/changed in the future. - */ - if (m_recircPortRole.find(alias) != m_recircPortRole.end()) - { - it = consumer.m_toSync.erase(it); - continue; - } - if (!an_str.empty()) { if (autoneg_mode_map.find(an_str) == autoneg_mode_map.end()) @@ -5955,80 +5945,6 @@ bool PortsOrch::getRecircPort(Port &port, string role) return false; } -bool PortsOrch::doProcessRecircPort(string alias, string role, set lane_set, string op) -{ - SWSS_LOG_ENTER(); - - if (op == SET_COMMAND) - { - if (m_recircPortRole.find(alias) != m_recircPortRole.end()) - { - SWSS_LOG_DEBUG("Recirc port %s already added", alias.c_str()); - return true; - } - - /* Find pid of recirc port */ - sai_object_id_t port_id = SAI_NULL_OBJECT_ID; - if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end()) - { - port_id = m_portListLaneMap[lane_set]; - } - - if (port_id == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("Failed to find port id for recirc port %s", alias.c_str()); - return false; - } - - Port p(alias, Port::PHY); - p.m_port_id = port_id; - p.m_init = true; - m_recircPortRole[alias] = role; - setPort(alias, p); - - string lane_str = ""; - for (auto lane : lane_set) - { - lane_str += to_string(lane) + " "; - } - SWSS_LOG_NOTICE("Added recirc port %s, pid:%" PRIx64 " lanes:%s", - alias.c_str(), port_id, lane_str.c_str()); - - /* Create host intf for recirc port */ - if(addHostIntfs(p, p.m_alias, p.m_hif_id)) - { - SWSS_LOG_NOTICE("Created host intf for recycle port %s", p.m_alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to Create host intf for recirc port %s", p.m_alias.c_str()); - } - - if(setHostIntfsOperStatus(p, true)) - { - SWSS_LOG_NOTICE("Set host intf oper status UP for recirc port %s", p.m_alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to set host intf oper status for recirc port %s", p.m_alias.c_str()); - } - - PortUpdate update = { p, true }; - notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); - return true; - } - else if (op == DEL_COMMAND) - { - SWSS_LOG_ERROR("Delete recirc port is not supported."); - return false; - } - else - { - SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - return false; - } -} - bool PortsOrch::addSystemPorts() { vector keys; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index c29b59837f..22efce3561 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -324,7 +324,6 @@ class PortsOrch : public Orch, public Subject bool initGearboxPort(Port &port); map m_recircPortRole; - bool doProcessRecircPort(string alias, string role, set laneSet, string op); //map key is tuple of map, sai_object_id_t> m_systemPortOidMap; From 5295f91be9e07525a462cdaba894bebfe0ded0a2 Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Wed, 7 Jul 2021 10:19:45 -0700 Subject: [PATCH 05/28] Add failure handling for SAI get operations (#1768) What I did Add failure handling for SAI get operations. The function allows handling failures in SAI get operations according to the orch type, SAI type, SAI status. Why I did it Enable custom failure handling for SAI get operations. --- orchagent/aclorch.cpp | 6 +- orchagent/copporch.cpp | 6 +- orchagent/crmorch.cpp | 12 +++- orchagent/fabricportsorch.cpp | 36 ++++++++-- orchagent/fdborch.cpp | 6 +- orchagent/fgnhgorch.cpp | 10 ++- orchagent/macsecorch.cpp | 15 ++-- orchagent/neighorch.cpp | 6 +- orchagent/orch.cpp | 29 ++++++++ orchagent/orch.h | 1 + orchagent/portsorch.cpp | 126 ++++++++++++++++++++++++++++------ orchagent/qosorch.cpp | 24 +++++-- 12 files changed, 233 insertions(+), 44 deletions(-) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 7f2ccfb50c..a2f5482a36 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -2309,7 +2309,11 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr else { SWSS_LOG_ERROR("Failed to get ACL entry priority min/max values, rv:%d", status); - throw "AclOrch initialization failure"; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw "AclOrch initialization failure"; + } } queryAclActionCapability(); diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index 403fcb98d9..34d83dd274 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -179,7 +179,11 @@ void CoppOrch::initDefaultTrapGroup() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get default trap group, rv:%d", status); - throw "CoppOrch initialization failure"; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw "CoppOrch initialization failure"; + } } SWSS_LOG_INFO("Get default trap group"); diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index e0eb24239b..bdd899057a 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -488,7 +488,11 @@ void CrmOrch::getResAvailableCounters() break; } SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - break; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + break; + } } res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = attr.value.u32; @@ -517,7 +521,11 @@ void CrmOrch::getResAvailableCounters() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - break; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + break; + } } for (uint32_t i = 0; i < attr.value.aclresource.count; i++) diff --git a/orchagent/fabricportsorch.cpp b/orchagent/fabricportsorch.cpp index a4644dfffc..1adb84ec08 100644 --- a/orchagent/fabricportsorch.cpp +++ b/orchagent/fabricportsorch.cpp @@ -88,7 +88,11 @@ int FabricPortsOrch::getFabricPortList() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get fabric port number, rv:%d", status); - return FABRIC_PORT_ERROR; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + return FABRIC_PORT_ERROR; + } } m_fabricPortCount = attr.value.u32; SWSS_LOG_NOTICE("Get %d fabric ports", m_fabricPortCount); @@ -101,7 +105,11 @@ int FabricPortsOrch::getFabricPortList() status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get port list failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get port list failure"); + } } for (i = 0; i < m_fabricPortCount; i++) @@ -113,7 +121,11 @@ int FabricPortsOrch::getFabricPortList() status = sai_port_api->get_port_attribute(fabric_port_list[i], 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get port lane failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get port lane failure"); + } } int lane = attr.value.u32list.list[0]; m_fabricLanePortMap[lane] = fabric_port_list[i]; @@ -198,7 +210,11 @@ void FabricPortsOrch::updateFabricPortState() { // Port may not be ready for query SWSS_LOG_ERROR("Failed to get fabric port (%d) status, rv:%d", lane, status); - return; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return; + } } if (m_portStatus.find(lane) != m_portStatus.end() && @@ -215,7 +231,11 @@ void FabricPortsOrch::updateFabricPortState() status = sai_port_api->get_port_attribute(port, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get remote id failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get remote id failure"); + } } remote_peer = attr.value.u32; @@ -223,7 +243,11 @@ void FabricPortsOrch::updateFabricPortState() status = sai_port_api->get_port_attribute(port, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get remote port index failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get remote port index failure"); + } } remote_port = attr.value.u32; } diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index af46d958ec..229dec0b15 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -503,7 +503,11 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) { SWSS_LOG_ERROR("Failed to get bridge port ID for FDB entry %s, rv:%d", mac.to_string().c_str(), status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_FDB, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } if (!m_portsOrch->getPortByBridgePortId(attr.value.oid, port)) diff --git a/orchagent/fgnhgorch.cpp b/orchagent/fgnhgorch.cpp index de791678ac..4111665e09 100644 --- a/orchagent/fgnhgorch.cpp +++ b/orchagent/fgnhgorch.cpp @@ -294,11 +294,15 @@ bool FgNhgOrch::createFineGrainedNextHopGroup(FGNextHopGroupEntry &syncd_fg_rout { SWSS_LOG_ERROR("Failed to query next hop group %s SAI_NEXT_HOP_GROUP_ATTR_REAL_SIZE, rv:%d", nextHops.to_string().c_str(), status); - if (!removeFineGrainedNextHopGroup(&syncd_fg_route_entry)) + task_process_status handle_status = handleSaiGetStatus(SAI_API_NEXT_HOP_GROUP, status); + if (handle_status != task_process_status::task_success) { - SWSS_LOG_ERROR("Failed to clean-up after next hop group real_size query failure"); + if (!removeFineGrainedNextHopGroup(&syncd_fg_route_entry)) + { + SWSS_LOG_ERROR("Failed to clean-up after next hop group real_size query failure"); + } + return false; } - return false; } fgNhgEntry->real_bucket_size = nhg_attr.value.u32; } diff --git a/orchagent/macsecorch.cpp b/orchagent/macsecorch.cpp index c5510a16fa..ead1bfa81c 100644 --- a/orchagent/macsecorch.cpp +++ b/orchagent/macsecorch.cpp @@ -854,15 +854,20 @@ bool MACsecOrch::initMACsecObject(sai_object_id_t switch_id) attrs.clear(); attr.id = SAI_MACSEC_ATTR_SCI_IN_INGRESS_MACSEC_ACL; attrs.push_back(attr); - if (sai_macsec_api->get_macsec_attribute( - macsec_obj.first->second.m_ingress_id, - static_cast(attrs.size()), - attrs.data()) != SAI_STATUS_SUCCESS) + status = sai_macsec_api->get_macsec_attribute( + macsec_obj.first->second.m_ingress_id, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_WARN( "Cannot get MACsec attribution SAI_MACSEC_ATTR_SCI_IN_INGRESS_MACSEC_ACL at the switch 0x%" PRIx64, switch_id); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_MACSEC, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } macsec_obj.first->second.m_sci_in_ingress_macsec_acl = attrs.front().value.booldata; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index acd8d8718b..b9870a24eb 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -1547,7 +1547,11 @@ void NeighOrch::voqSyncAddNeigh(string &alias, IpAddress &ip_address, const MacA if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get neighbor attribute for %s on %s, rv:%d", ip_address.to_string().c_str(), alias.c_str(), status); - return; + task_process_status handle_status = handleSaiGetStatus(SAI_API_NEIGHBOR, status); + if (handle_status != task_process_status::task_success) + { + return; + } } if (!attr.value.u32) diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 464e6aa688..14187b79b4 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -765,6 +765,35 @@ task_process_status Orch::handleSaiRemoveStatus(sai_api_t api, sai_status_t stat return task_need_retry; } +task_process_status Orch::handleSaiGetStatus(sai_api_t api, sai_status_t status, void *context) +{ + /* + * This function aims to provide coarse handling of failures in sairedis get + * operation (i.e., notify users by throwing excepions when failures happen). + * Return value: task_success - Handled the status successfully. No need to retry this SAI operation. + * task_need_retry - Cannot handle the status. Need to retry the SAI operation. + * task_failed - Failed to handle the status but another attempt is unlikely to resolve the failure. + * 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 handleSaiGetStatus"); + return task_success; + case SAI_STATUS_NOT_IMPLEMENTED: + SWSS_LOG_ERROR("Encountered failure in get operation due to the function is not implemented, exiting orchagent, SAI API: %s", + sai_serialize_api(api).c_str()); + throw std::logic_error("SAI get function not implemented"); + default: + SWSS_LOG_ERROR("Encountered failure in get operation, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + } + return task_failed; +} + bool Orch::parseHandleSaiStatusFailure(task_process_status status) { /* diff --git a/orchagent/orch.h b/orchagent/orch.h index b61cdb53e2..766d02c766 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -240,6 +240,7 @@ class Orch virtual task_process_status handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context = nullptr); virtual task_process_status handleSaiSetStatus(sai_api_t api, sai_status_t status, void *context = nullptr); virtual task_process_status handleSaiRemoveStatus(sai_api_t api, sai_status_t status, void *context = nullptr); + virtual task_process_status handleSaiGetStatus(sai_api_t api, sai_status_t status, void *context = nullptr); bool parseHandleSaiStatusFailure(task_process_status status); 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); diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 9b22a551b5..fab6a7ed59 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -380,7 +380,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector tmp_lane_set; @@ -459,7 +475,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vectorget_port_attribute(id, 1, &attr); if (status == SAI_STATUS_SUCCESS) + { speed = attr.value.u32; + } + else + { + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } + } - return status == SAI_STATUS_SUCCESS; + return true; } bool PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list) @@ -1994,7 +2040,11 @@ bool PortsOrch::getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uin if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get queue type and index for queue %" PRIu64 " rv:%d", queue_id, status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_QUEUE, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } switch (attr[0].value.s32) @@ -3850,7 +3900,11 @@ void PortsOrch::initializeQueues(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get number of queues for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get %d queues for port %s", attr.value.u32, port.m_alias.c_str()); @@ -3870,7 +3924,11 @@ void PortsOrch::initializeQueues(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get queue list for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get queues for port %s", port.m_alias.c_str()); @@ -3886,7 +3944,11 @@ void PortsOrch::initializePriorityGroups(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get number of priority groups for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get %d priority groups for port %s", attr.value.u32, port.m_alias.c_str()); @@ -3907,7 +3969,11 @@ void PortsOrch::initializePriorityGroups(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Fail to get priority group list for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get priority groups for port %s", port.m_alias.c_str()); } @@ -5403,7 +5469,11 @@ bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, { SWSS_LOG_ERROR("Failed to get port attr serdes id %d to port pid:0x%" PRIx64, port_attr.id, port_id); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } if (port_attr.value.oid != SAI_NULL_OBJECT_ID) @@ -5897,7 +5967,11 @@ bool PortsOrch::getSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port list, rv:%d", status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } uint32_t spcnt = attr.value.objlist.count; @@ -5909,7 +5983,11 @@ bool PortsOrch::getSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port config info spid:%" PRIx64, system_port_list[i]); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } SWSS_LOG_NOTICE("SystemPort(0x%" PRIx64 ") - port_id:%u, switch_id:%u, core:%u, core_port:%u, speed:%u, voqs:%u", @@ -6018,7 +6096,11 @@ bool PortsOrch::addSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port config info spid:%" PRIx64, system_port_oid); - continue; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + continue; + } } //Create or update system port and add to the port list. @@ -6037,7 +6119,11 @@ bool PortsOrch::addSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get local port oid of local system port spid:%" PRIx64, system_port_oid); - continue; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + continue; + } } //System port for local port. Update the system port info in the existing physical port diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 31e61b5433..c2e15aa763 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -933,7 +933,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get number of scheduler groups for port:%s", port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } /* Get total groups list on the port */ @@ -947,7 +951,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get scheduler group list for port:%s", port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } m_scheduler_group_port_info[port.m_port_id] = { @@ -969,7 +977,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SCHEDULER_GROUP, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } uint32_t child_count = attr.value.u32; @@ -988,7 +1000,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SCHEDULER_GROUP, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } m_scheduler_group_port_info[port.m_port_id].child_groups[ii] = std::move(child_groups); From ae447013e712e8114888a912732bbfb482275d0c Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Thu, 8 Jul 2021 02:29:44 +0800 Subject: [PATCH 06/28] [orchagent] Put port configuration to APPL_DB according to autoneg mode (#1769) *Before puting port related configuration to APPL_DB, check autoneg mode first. If autoneg mode is enabled, "speed" and "interface_type" will not be put into APPL_DB; if autoneg mode is disabled, "adv_speeds" and "adv_interface_types" will not be put into APPL_DB; else all configuration will be put to APPL_DB for backward compatible. --- orchagent/portsorch.cpp | 11 ++++----- portsyncd/portsyncd.cpp | 53 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index fab6a7ed59..87da4e6f73 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2625,26 +2625,23 @@ void PortsOrch::doPortTask(Consumer &consumer) { an_str = fvValue(i); } - /* Set advertised speeds */ - if (fvField(i) == "adv_speeds") + else if (fvField(i) == "adv_speeds") { adv_speeds_str = fvValue(i); } - /* Set interface type */ - if (fvField(i) == "interface_type") + else if (fvField(i) == "interface_type") { interface_type_str = fvValue(i); } - /* Set advertised interface type */ - if (fvField(i) == "adv_interface_types") + else if (fvField(i) == "adv_interface_types") { adv_interface_types_str = fvValue(i); } /* Set port serdes Pre-emphasis */ - if (fvField(i) == "preemphasis") + else if (fvField(i) == "preemphasis") { getPortSerdesVal(fvValue(i), attr_val); serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_PREEMPHASIS, attr_val)); diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index 151ac16657..c66fe7db52 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -235,6 +235,10 @@ bool handlePortConfigFromConfigDB(ProducerStateTable &p, DBConnector &cfgDb, boo void handlePortConfig(ProducerStateTable &p, map &port_cfg_map) { + string autoneg; + vector attrs; + vector autoneg_attrs; + vector force_attrs; auto it = port_cfg_map.begin(); while (it != port_cfg_map.end()) @@ -250,7 +254,54 @@ void handlePortConfig(ProducerStateTable &p, map /* No support for port delete yet */ if (op == SET_COMMAND) { - p.set(key, values); + + for (auto i : values) + { + auto field = fvField(i); + if (field == "adv_speeds") + { + autoneg_attrs.push_back(i); + } + else if (field == "adv_interface_types") + { + autoneg_attrs.push_back(i); + } + else if (field == "speed") + { + force_attrs.push_back(i); + } + else if (field == "interface_type") + { + force_attrs.push_back(i); + } + else if (field == "autoneg") + { + autoneg = fvValue(i); + attrs.push_back(i); + } + else + { + attrs.push_back(i); + } + } + if (autoneg == "on") // autoneg is on, only put adv_speeds and adv_interface_types to APPL_DB + { + attrs.insert(attrs.end(), autoneg_attrs.begin(), autoneg_attrs.end()); + } + else if (autoneg == "off") // autoneg is off, only put speed and interface_type to APPL_DB + { + attrs.insert(attrs.end(), force_attrs.begin(), force_attrs.end()); + } + else // autoneg is not configured, put all attributes to APPL_DB + { + attrs.insert(attrs.end(), autoneg_attrs.begin(), autoneg_attrs.end()); + attrs.insert(attrs.end(), force_attrs.begin(), force_attrs.end()); + } + p.set(key, attrs); + attrs.clear(); + autoneg_attrs.clear(); + force_attrs.clear(); + autoneg.clear(); } it = port_cfg_map.erase(it); From 4f1d726d4cbf8a283b22cd5f612cf03ca21a27b3 Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak <38952541+stepanblyschak@users.noreply.github.com> Date: Thu, 8 Jul 2021 10:27:02 +0300 Subject: [PATCH 07/28] [portsorch] fix errors when moving port from one lag to another. (#1797) In scenario that is executed in sonic-mgmt in test_po_update.py a portchannel member is deleted from one portchannel and added to another portchannel. It is possible that requests from teamsynd will arrive in different order This reordering happens because teamsyncd has single event handler/selectable TeamSync::TeamPortSync::onChange() per team device so when two of them are ready it is swss::Select implementation detail in which order they are going to be returned. This is a fundamental issue of Producer/ConsumerStateTable, thus orchagent must be aware of this and treat it as normal situation and figure out the right order and not crash or print an errors. - What I did Check if port is already a lag member beforehand. Added an UT to cover this scenario, this UT verifies that SAI API is not called in this case. Refactored portsorch_ut.cpp by moving out Orchs creation/deletion into SetUp()/TearDown() - Why I did it To fix errors in log. - How I verified it Ran test_po_update.py test. Signed-off-by: Stepan Blyschak stepanb@nvidia.com --- orchagent/portsorch.cpp | 51 +++++-- tests/mock_tests/portsorch_ut.cpp | 238 ++++++++++++++++++------------ 2 files changed, 176 insertions(+), 113 deletions(-) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 87da4e6f73..4ac36258b9 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -266,6 +266,12 @@ static char* hostif_vlan_tag[] = { [SAI_HOSTIF_VLAN_TAG_KEEP] = "SAI_HOSTIF_VLAN_TAG_KEEP", [SAI_HOSTIF_VLAN_TAG_ORIGINAL] = "SAI_HOSTIF_VLAN_TAG_ORIGINAL" }; + +static bool isValidPortTypeForLagMember(const Port& port) +{ + return (port.m_type == Port::Type::PHY || port.m_type == Port::Type::SYSTEM); +} + /* * Initialize PortsOrch * 0) If Gearbox is enabled, then initialize the external PHYs as defined in @@ -1758,7 +1764,7 @@ void PortsOrch::getPortSupportedSpeeds(const std::string& alias, sai_object_id_t } // if our guess was wrong, retry with the correct value - speeds.resize(attr.value.u32list.count); + speeds.resize(attr.value.u32list.count); } if (status == SAI_STATUS_SUCCESS) @@ -2243,7 +2249,7 @@ sai_status_t PortsOrch::removePort(sai_object_id_t port_id) Port port; - /* + /* * Make sure to bring down admin state. * SET would have replaced with DEL */ @@ -2812,7 +2818,7 @@ void PortsOrch::doPortTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } - + an = autoneg_mode_map[an_str]; if (an != p.m_autoneg) { @@ -2878,7 +2884,7 @@ void PortsOrch::doPortTask(Consumer &consumer) it++; continue; } - + SWSS_LOG_NOTICE("Set port %s speed from %u to %u", alias.c_str(), p.m_speed, speed); p.m_speed = speed; m_portList[alias] = p; @@ -2919,7 +2925,7 @@ void PortsOrch::doPortTask(Consumer &consumer) auto ori_adv_speeds = swss::join(',', p.m_adv_speeds.begin(), p.m_adv_speeds.end()); if (!setPortAdvSpeeds(p.m_port_id, adv_speeds)) { - + SWSS_LOG_ERROR("Failed to set port %s advertised speed from %s to %s", alias.c_str(), ori_adv_speeds.c_str(), adv_speeds_str.c_str()); @@ -3713,6 +3719,15 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) continue; } + /* Fail if a port type is not a valid type for being a LAG member port. + * Erase invalid entry, no need to retry in this case. */ + if (!isValidPortTypeForLagMember(port)) + { + SWSS_LOG_ERROR("LAG member port has to be of type PHY or SYSTEM"); + it = consumer.m_toSync.erase(it); + continue; + } + if (table_name == CHASSIS_APP_LAG_MEMBER_TABLE_NAME) { int32_t lag_switch_id = lag.m_system_lag_info.switch_id; @@ -3746,8 +3761,12 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) if (lag.m_members.find(port_alias) == lag.m_members.end()) { - /* Assert the port doesn't belong to any LAG already */ - assert(!port.m_lag_id && !port.m_lag_member_id); + if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Port %s is already a LAG member", port.m_alias.c_str()); + it++; + continue; + } if (!addLagMember(lag, port, (status == "enabled"))) { @@ -5567,7 +5586,7 @@ void PortsOrch::getPortSerdesVal(const std::string& val_str, } } -bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, +bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, std::vector &speed_values) { SWSS_LOG_ENTER(); @@ -5581,7 +5600,7 @@ bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, std::string speed_str; std::istringstream iss(val_str); - try + try { while (std::getline(iss, speed_str, ',')) { @@ -5598,31 +5617,31 @@ bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, return true; } -bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, +bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type) { SWSS_LOG_ENTER(); auto iter = interface_type_map_for_an.find(s); - if (iter != interface_type_map_for_an.end()) + if (iter != interface_type_map_for_an.end()) { interface_type = interface_type_map_for_an[s]; return true; } - else + else { const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse interface_type value %s, valid interface type includes: %s", + SWSS_LOG_ERROR("Failed to parse interface_type value %s, valid interface type includes: %s", s.c_str(), validInterfaceTypes.c_str()); return false; } } -bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, +bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, std::vector &type_values) { SWSS_LOG_ENTER(); - if (val_str == "all") + if (val_str == "all") { return true; } @@ -5637,7 +5656,7 @@ bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, valid = getPortInterfaceTypeVal(type_str, interface_type); if (!valid) { const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse adv_interface_types value %s, valid interface type includes: %s", + SWSS_LOG_ERROR("Failed to parse adv_interface_types value %s, valid interface type includes: %s", val_str.c_str(), validInterfaceTypes.c_str()); return false; } diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 32b1d373ee..2c65a42f09 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -1,3 +1,7 @@ +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private + #include "ut_helper.h" #include "mock_orchagent_main.h" #include "mock_table.h" @@ -36,17 +40,51 @@ namespace portsorch_test virtual void SetUp() override { ::testing_db::reset(); + + // Create dependencies ... + + const int portsorch_base_pri = 40; + + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + ASSERT_EQ(gPortsOrch, nullptr); + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(flexCounterOrch); + + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; + + ASSERT_EQ(gBufferOrch, nullptr); + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); } virtual void TearDown() override { ::testing_db::reset(); + delete gPortsOrch; gPortsOrch = nullptr; delete gBufferOrch; gBufferOrch = nullptr; - } + // clear orchs saved in directory + gDirectory.m_values.clear(); + } static void SetUpTestCase() { // Init switch and create dependencies @@ -92,6 +130,7 @@ namespace portsorch_test ut_helper::uninitSaiApi(); } + }; TEST_F(PortsOrchTest, PortReadinessColdBoot) @@ -132,27 +171,7 @@ namespace portsorch_test pgTableCfg.set(ossCfg.str(), { { "profile", "[BUFFER_PROFILE|test_profile]" } }); } - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - - vector flex_counter_tables = { - CFG_FLEX_COUNTER_TABLE_NAME - }; - auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); - gDirectory.set(flexCounterOrch); - - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + // Recreate buffer orch to read populated data vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, APP_BUFFER_PROFILE_TABLE_NAME, APP_BUFFER_QUEUE_TABLE_NAME, @@ -160,7 +179,6 @@ namespace portsorch_test APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - ASSERT_EQ(gBufferOrch, nullptr); gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); // Populate pot table with SAI ports @@ -223,7 +241,6 @@ namespace portsorch_test TEST_F(PortsOrchTest, PortReadinessWarmBoot) { - Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); Table pgTable = Table(m_app_db.get(), APP_BUFFER_PG_TABLE_NAME); Table profileTable = Table(m_app_db.get(), APP_BUFFER_PROFILE_TABLE_NAME); @@ -268,30 +285,6 @@ namespace portsorch_test portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); portTable.set("PortInitDone", { { "lanes", "0" } }); - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - // warm start, bake fill refill consumer gBufferOrch->bake(); @@ -338,30 +331,6 @@ namespace portsorch_test // Get SAI default ports to populate DB auto ports = ut_helper::getInitialSaiPorts(); - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - // Populate port table with SAI ports for (const auto &it : ports) { @@ -460,6 +429,106 @@ namespace portsorch_test ts.clear(); } + /* This test checks that a LAG member validation happens on orchagent level + * and no SAI call is executed in case a port requested to be a LAG member + * is already a LAG member. + */ + TEST_F(PortsOrchTest, LagMemberDoesNotCallSAIApiWhenPortIsAlreadyALagMember) + { + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table lagTable = Table(m_app_db.get(), APP_LAG_TABLE_NAME); + Table lagMemberTable = Table(m_app_db.get(), APP_LAG_MEMBER_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + /* + * Next we will prepare some configuration data to be consumed by PortsOrch + * 32 Ports, 2 LAGs, 1 port is LAG member. + */ + + // Populate pot table with SAI ports + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + portTable.set("PortInitDone", { { } }); + + lagTable.set("PortChannel999", + { + {"admin_status", "up"}, + {"mtu", "9100"} + } + ); + lagTable.set("PortChannel0001", + { + {"admin_status", "up"}, + {"mtu", "9100"} + } + ); + lagMemberTable.set( + std::string("PortChannel999") + lagMemberTable.getTableNameSeparator() + ports.begin()->first, + { {"status", "enabled"} }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + gPortsOrch->addExistingData(&lagTable); + gPortsOrch->addExistingData(&lagMemberTable); + + static_cast(gPortsOrch)->doTask(); + + // check LAG, VLAN tasks were processed + // port table may require one more doTask iteration + for (auto tableName: {APP_LAG_TABLE_NAME, APP_LAG_MEMBER_TABLE_NAME}) + { + vector ts; + auto exec = gPortsOrch->getExecutor(tableName); + auto consumer = static_cast(exec); + ts.clear(); + consumer->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + } + + // Set first port as a LAG member while this port is still a member of different LAG. + lagMemberTable.set( + std::string("PortChannel0001") + lagMemberTable.getTableNameSeparator() + ports.begin()->first, + { {"status", "enabled"} }); + + // save original api since we will spy + auto orig_lag_api = sai_lag_api; + sai_lag_api = new sai_lag_api_t(); + memcpy(sai_lag_api, orig_lag_api, sizeof(*sai_lag_api)); + + bool lagMemberCreateCalled = false; + + auto lagSpy = SpyOn(&sai_lag_api->create_lag_member); + lagSpy->callFake([&](sai_object_id_t *oid, sai_object_id_t swoid, uint32_t count, const sai_attribute_t * attrs) -> sai_status_t + { + lagMemberCreateCalled = true; + return orig_lag_api->create_lag_member(oid, swoid, count, attrs); + } + ); + + gPortsOrch->addExistingData(&lagMemberTable); + + static_cast(gPortsOrch)->doTask(); + sai_lag_api = orig_lag_api; + + // verify there is a pending task to do. + vector ts; + auto exec = gPortsOrch->getExecutor(APP_LAG_MEMBER_TABLE_NAME); + auto consumer = static_cast(exec); + ts.clear(); + consumer->dumpPendingTasks(ts); + ASSERT_FALSE(ts.empty()); + + // verify there was no SAI call executed. + ASSERT_FALSE(lagMemberCreateCalled); + } + /* * The scope of this test is to verify that LAG member is * added to a LAG before any other object on LAG is created, like RIF, bridge port in warm mode. @@ -474,7 +543,6 @@ namespace portsorch_test */ TEST_F(PortsOrchTest, LagMemberIsCreatedBeforeOtherObjectsAreCreatedOnLag) { - Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); Table lagTable = Table(m_app_db.get(), APP_LAG_TABLE_NAME); Table lagMemberTable = Table(m_app_db.get(), APP_LAG_MEMBER_TABLE_NAME); @@ -484,29 +552,6 @@ namespace portsorch_test // Get SAI default ports to populate DB auto ports = ut_helper::getInitialSaiPorts(); - // Create dependencies ... - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - /* * Next we will prepare some configuration data to be consumed by PortsOrch * 32 Ports, 1 LAG, 1 port is LAG member and LAG is in Vlan. @@ -598,5 +643,4 @@ namespace portsorch_test ASSERT_FALSE(bridgePortCalledBeforeLagMember); // bridge port created on lag before lag member was created } - } From 64e33b38c8c3f242745f5541c4f74bf2260c7d56 Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Thu, 8 Jul 2021 13:25:35 -0700 Subject: [PATCH 08/28] Ignore ALREADY_EXIST error in FDB creation (#1815) What I did Ignore ALREADY_EXIST error in FDB creation. Fix: Azure/sonic-buildimage#7798 Why I did it In FDB creation, there are scenarios where the hardware learns an FDB entry before orchagent. In such cases, the FDB SAI creation would report the status of SAI_STATUS_ITEM_ALREADY_EXISTS, and orchagent should ignore the error and treat it as entry was explicitly created. --- orchagent/orch.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 14187b79b4..2afc2c20f4 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -699,15 +699,38 @@ task_process_status Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t stat * in each orch. * 3. Take the type of sai api into consideration. */ - switch (status) + switch (api) { - case SAI_STATUS_SUCCESS: - SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); - return task_success; + case SAI_API_FDB: + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return task_success; + case SAI_STATUS_ITEM_ALREADY_EXISTS: + /* + * In FDB creation, there are scenarios where the hardware learns an FDB entry before orchagent. + * In such cases, the FDB SAI creation would report the status of SAI_STATUS_ITEM_ALREADY_EXISTS, + * and orchagent should ignore the error and treat it as entry was explicitly created. + */ + return task_success; + 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); + } + break; 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); + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return task_success; + 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 task_need_retry; } From 3b6620b29de9db4d5b9def6316a4356304f9f4b2 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Wed, 14 Jul 2021 22:18:29 +0000 Subject: [PATCH 09/28] Fixing compilation --- orchagent/main.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/orchagent/main.cpp b/orchagent/main.cpp index bb8c915fc4..3028c80c59 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -669,7 +669,6 @@ int main(int argc, char **argv) } } } - auto orchDaemon = make_shared(&appl_db, &config_db, &state_db, chassis_app_db.get()); if (!orchDaemon->init()) { From 5d97b0571eaf3157cbf9fd74748ab7b51c18706d Mon Sep 17 00:00:00 2001 From: Ze Gan Date: Thu, 15 Jul 2021 14:16:39 +0800 Subject: [PATCH 10/28] Update MACsec SA PN counter to support SAI API 1.8 (#1818) What I did Rename XPN attributes from SAI_MACSEC_SA_ATTR_MINIMUM_XPN and SAI_MACSEC_SA_ATTR_XPN to SAI_MACSEC_SA_ATTR_CURRENT_XPN Why I did it Due to opencomputeproject/SAI#1169 that refactors the attributes about XPN, a new attributes SAI_MACSEC_SA_ATTR_CURRENT_XPN for both ingress and egress was introduced for reading the Packet number. So move the original attributes to the new one. Signed-off-by: Ze Gan --- orchagent/macsecorch.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/orchagent/macsecorch.cpp b/orchagent/macsecorch.cpp index ead1bfa81c..2247deba84 100644 --- a/orchagent/macsecorch.cpp +++ b/orchagent/macsecorch.cpp @@ -31,14 +31,9 @@ constexpr bool DEFAULT_ENABLE_ENCRYPT = true; constexpr bool DEFAULT_SCI_IN_SECTAG = false; constexpr sai_macsec_cipher_suite_t DEFAULT_CIPHER_SUITE = SAI_MACSEC_CIPHER_SUITE_GCM_AES_128; -static const std::vector macsec_egress_sa_attrs = +static const std::vector macsec_sa_attrs = { - "SAI_MACSEC_SA_ATTR_XPN", -}; - -static const std::vector macsec_ingress_sa_attrs = - { - "SAI_MACSEC_SA_ATTR_MINIMUM_XPN", + "SAI_MACSEC_SA_ATTR_CURRENT_XPN", }; template @@ -1743,16 +1738,15 @@ task_process_status MACsecOrch::createMACsecSA( sc->m_sa_ids.erase(an); }); + installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_sa_attrs); std::vector fvVector; fvVector.emplace_back("state", "ok"); if (direction == SAI_MACSEC_DIRECTION_EGRESS) { - installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_egress_sa_attrs); m_state_macsec_egress_sa.set(swss::join('|', port_name, sci, an), fvVector); } else { - installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_ingress_sa_attrs); m_state_macsec_ingress_sa.set(swss::join('|', port_name, sci, an), fvVector); } From 9f0bb8d20c4343556d1eacdddab451141d9c6818 Mon Sep 17 00:00:00 2001 From: Lior Avramov <73036155+liorghub@users.noreply.github.com> Date: Sat, 17 Jul 2021 04:30:39 +0300 Subject: [PATCH 11/28] [swss]: Allow portsyncd to run on system without ports (#1808) Remove Portsyncd error exit if there are no ports in config DB. Signed-off-by: liora --- portsyncd/portsyncd.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index c66fe7db52..beaa008449 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -88,10 +88,8 @@ int main(int argc, char **argv) if (!handlePortConfigFromConfigDB(p, cfgDb, warm)) { - // if port config is missing in ConfigDB - // program will exit with failure - SWSS_LOG_THROW("ConfigDB does not have port information, exiting..."); - return EXIT_FAILURE; + SWSS_LOG_NOTICE("ConfigDB does not have port information, " + "however ports can be added later on, continuing..."); } LinkSync sync(&appl_db, &state_db); From ed6786d4034c21363c9da5abb6e7303a6a8a35af Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak <38952541+stepanblyschak@users.noreply.github.com> Date: Tue, 20 Jul 2021 03:38:56 +0300 Subject: [PATCH 12/28] =?UTF-8?q?[debugcounterorch]=20check=20if=20counter?= =?UTF-8?q?=20type=20is=20supported=20before=20querying=E2=80=A6=20(#1789)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [debugcounterorch] check if counter type is supported before querying object availability count Signed-off-by: Stepan Blyschak --- orchagent/debug_counter/drop_counter.cpp | 57 ++++++++++++++++++++++++ orchagent/debug_counter/drop_counter.h | 1 + orchagent/debugcounterorch.cpp | 9 +++- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/orchagent/debug_counter/drop_counter.cpp b/orchagent/debug_counter/drop_counter.cpp index db62574614..db80c894ab 100644 --- a/orchagent/debug_counter/drop_counter.cpp +++ b/orchagent/debug_counter/drop_counter.cpp @@ -342,6 +342,63 @@ unordered_set DropCounter::getSupportedDropReasons(sai_debug_counter_att return supported_drop_reasons; } +// Returns a set of supported counter types. +unordered_set DropCounter::getSupportedCounterTypes() +{ + sai_status_t status = SAI_STATUS_FAILURE; + + const auto& countersTypeLookup = getDebugCounterTypeLookup(); + unordered_set supportedCounterTypes; + + sai_s32_list_t enumValuesCapabilities; + vector saiCounterTypes; + + const auto* meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_DEBUG_COUNTER, + SAI_DEBUG_COUNTER_ATTR_TYPE); + if (!meta) + { + SWSS_LOG_ERROR("SAI BUG: metadata null pointer returned by " + "sai_metadata_get_attr_metadata for SAI_DEBUG_COUNTER_ATTR_TYPE"); + return {}; + } + + if (!meta->isenum || !meta->enummetadata) + { + SWSS_LOG_ERROR("SAI BUG: SAI_DEBUG_COUNTER_ATTR_TYPE value type is not an enum"); + return {}; + } + + saiCounterTypes.assign(meta->enummetadata->valuescount, 0); + + enumValuesCapabilities.count = static_cast(saiCounterTypes.size()); + enumValuesCapabilities.list = saiCounterTypes.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_DEBUG_COUNTER, + SAI_DEBUG_COUNTER_ATTR_TYPE, + &enumValuesCapabilities); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support querying drop counters"); + return {}; + } + + for (uint32_t i = 0; i < enumValuesCapabilities.count; i++) + { + auto enumValue = static_cast(enumValuesCapabilities.list[i]); + for (const auto& it: countersTypeLookup) + { + if (it.second == enumValue) + { + supportedCounterTypes.emplace(it.first); + break; + } + } + } + + return supportedCounterTypes; +} + // serializeSupportedDropReasons takes a list of drop reasons and returns that // list as a string. // diff --git a/orchagent/debug_counter/drop_counter.h b/orchagent/debug_counter/drop_counter.h index bc548b34c7..7a151d139e 100644 --- a/orchagent/debug_counter/drop_counter.h +++ b/orchagent/debug_counter/drop_counter.h @@ -34,6 +34,7 @@ class DropCounter : public DebugCounter static std::unordered_set getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type); static std::string serializeSupportedDropReasons(std::unordered_set drop_reasons); + static std::unordered_set getSupportedCounterTypes(); static uint64_t getSupportedDebugCounterAmounts(sai_debug_counter_type_t counter_type); private: diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index ff2bee2e98..25d0b94589 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -183,6 +183,7 @@ void DebugCounterOrch::publishDropCounterCapabilities() { supported_ingress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST); supported_egress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST); + supported_counter_types = DropCounter::getSupportedCounterTypes(); string ingress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_ingress_drop_reasons); string egress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_egress_drop_reasons); @@ -190,6 +191,12 @@ void DebugCounterOrch::publishDropCounterCapabilities() for (auto const &counter_type : DebugCounter::getDebugCounterTypeLookup()) { string drop_reasons; + + if (!supported_counter_types.count(counter_type.first)) + { + continue; + } + if (counter_type.first == PORT_INGRESS_DROPS || counter_type.first == SWITCH_INGRESS_DROPS) { drop_reasons = ingress_drop_reason_str; @@ -213,8 +220,6 @@ void DebugCounterOrch::publishDropCounterCapabilities() continue; } - supported_counter_types.emplace(counter_type.first); - vector fieldValues; fieldValues.push_back(FieldValueTuple("count", num_counters)); fieldValues.push_back(FieldValueTuple("reasons", drop_reasons)); From c80502181f4a7bc5da11f1cdf9455443bb0fdd01 Mon Sep 17 00:00:00 2001 From: Mahesh Maddikayala <10645050+smaheshm@users.noreply.github.com> Date: Mon, 19 Jul 2021 22:59:37 -0700 Subject: [PATCH 13/28] [configure.ac] Add the option of passing libnl path to configure script (#1824) **What I did** Add the option of passing custmol built libnl path to configure script **Why I did it** MPLS feature in sonic-buildimage requires a libnl patch to be applied before building libnl. Since this build is not installed in usual locations (/usr/lib/..) LGTM analysis fails. This change gives the option of passing libnl library location to 'configure' script and generate libraries to be linked. In case the options are not passed, the configure script defaults to earlier behavior where it checks for LIBNL in usual locations --- configure.ac | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index d7816b6f17..edca67de7c 100644 --- a/configure.ac +++ b/configure.ac @@ -13,8 +13,6 @@ AC_HEADER_STDC AC_CHECK_LIB([hiredis], [redisConnect],, AC_MSG_ERROR([libhiredis is not installed.])) -AC_CHECK_LIB([nl-genl-3], [genl_connect]) - AC_CHECK_LIB([team], [team_alloc], AM_CONDITIONAL(HAVE_LIBTEAM, true), [AC_MSG_WARN([libteam is not installed.]) @@ -46,7 +44,27 @@ AC_ARG_WITH(extra-lib, prefix where extra libraries are installed], [AC_SUBST(LDFLAGS, "$LDFLAGS -L${withval}")]) -CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/libnl3 -I/usr/include/swss" +AC_ARG_WITH(extra-usr-lib, +[ --with-extra-usr-lib=DIR + prefix where extra libraries are installed], +[AC_SUBST(LDFLAGS, "$LDFLAGS -L${withval}")]) + +AC_CHECK_LIB([nl-3], [nl_addr_alloc]) +AC_CHECK_LIB([nl-genl-3], [nl_socket_get_cb]) +AC_CHECK_LIB([nl-route-3], [rtnl_route_nh_get_encap_mpls_dst]) +AC_CHECK_LIB([nl-nf-3], [nfnl_connect]) + +CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/swss" + +AC_ARG_WITH(libnl-3.0-inc, +[ --with-libnl-3.0-inc=DIR + prefix where libnl-3.0 includes are installed], +[AC_SUBST(CPPFLAGS, "$CPPFLAGS -I${withval}") + AC_SUBST(LIBNL_INC_DIR, "${withval}")]) + +if test "${with_libnl_3_0_inc+set}" != set; then + CFLAGS_COMMON+=" -I/usr/include/libnl3" +fi CFLAGS_COMMON+=" -Werror" CFLAGS_COMMON+=" -Wno-reorder" From 979860f5191bc73d0fe368ab05f24027adfc6975 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Wed, 21 Jul 2021 01:00:29 +0000 Subject: [PATCH 14/28] Added referencing logic --- orchagent/portsorch.cpp | 20 + orchagent/portsorch.h | 4 + orchagent/routeorch.cpp | 12 +- orchagent/vxlanorch.cpp | 171 ++++++++- orchagent/vxlanorch.h | 4 +- tests/test_evpn_p2p_tunnel.py | 690 ---------------------------------- 6 files changed, 184 insertions(+), 717 deletions(-) delete mode 100644 tests/test_evpn_p2p_tunnel.py diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 9e25694dac..3f38383f62 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -749,6 +749,24 @@ void PortsOrch::decreasePortRefCount(const string &alias) m_port_ref_count[alias]--; } +void PortsOrch::increaseBridgePortRefCount(Port &port) +{ + assert (m_bridge_port_ref_count.find(port.m_alias) != m_bridge_port_ref_count.end()); + m_bridge_port_ref_count[port.m_alias]++; +} + +void PortsOrch::decreaseBridgePortRefCount(Port &port) +{ + assert (m_bridge_port_ref_count.find(port.m_alias) != m_bridge_port_ref_count.end()); + m_bridge_port_ref_count[port.m_alias]--; +} + +bool PortsOrch::getBridgePortReferenceCount(Port &port) +{ + assert (m_bridge_port_ref_count.find(port.m_alias) != m_bridge_port_ref_count.end()); + return m_bridge_port_ref_count[port.m_alias]; +} + bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port) { SWSS_LOG_ENTER(); @@ -4532,6 +4550,7 @@ bool PortsOrch::addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip) } vlan.m_vlan_info.l2mc_members[end_point_ip] = l2mc_group_member; m_portList[vlan.m_alias] = vlan; + increaseBridgePortRefCount(port); return true; } @@ -4645,6 +4664,7 @@ bool PortsOrch::removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); return false; } + decreaseBridgePortRefCount(port); vlan.m_vlan_info.l2mc_members.erase(end_point_ip); sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; sai_attribute_t attr; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 043a6d2912..f598ab9058 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -145,6 +145,9 @@ class PortsOrch : public Orch, public Subject bool addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip); bool createVlanMember(Port &vlan, Port &port, sai_vlan_tagging_mode_t &sai_tagging_mode, sai_object_id_t &vlan_member_id); bool removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip); + void increaseBridgePortRefCount(Port &port); + void decreaseBridgePortRefCount(Port &port); + bool getBridgePortReferenceCount(Port &port); string m_inbandPortName = ""; bool isInbandPort(const string &alias); @@ -222,6 +225,7 @@ class PortsOrch : public Orch, public Subject uint32_t max_flood_control_types = 4; set uuc_sup_flood_control_type; set bc_sup_flood_control_type; + map m_bridge_port_ref_count; NotificationConsumer* m_portStatusNotificationConsumer; diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 5e7940b4ec..90ced877d9 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -2043,16 +2043,12 @@ bool RouteOrch::createRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH bool status = false; int ip_refcnt = 0; - if (!tunnel_orch->dipTunnelsUsed()) - { - return true; - } status = tunnel_orch->addTunnelUser(nextHop.ip_address.to_string(), nextHop.vni, 0, TUNNEL_USER_IP, vrf_id); auto vtep_ptr = evpn_orch->getEVPNVtep(); if (vtep_ptr) { - ip_refcnt = vtep_ptr->getDipTunnelIPRefCnt(nextHop.ip_address.to_string()); + ip_refcnt = vtep_ptr->getRemoteEndPointIPRefCnt(nextHop.ip_address.to_string()); } SWSS_LOG_INFO("Routeorch Add Remote VTEP %s, VNI %d, VR_ID %" PRIx64 ", IP ref_cnt %d", nextHop.ip_address.to_string().c_str(), nextHop.vni, vrf_id, ip_refcnt); @@ -2067,16 +2063,12 @@ bool RouteOrch::deleteRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH bool status = false; int ip_refcnt = 0; - if (!tunnel_orch->dipTunnelsUsed()) - { - return true; - } status = tunnel_orch->delTunnelUser(nextHop.ip_address.to_string(), nextHop.vni, 0, TUNNEL_USER_IP, vrf_id); auto vtep_ptr = evpn_orch->getEVPNVtep(); if (vtep_ptr) { - ip_refcnt = vtep_ptr->getDipTunnelIPRefCnt(nextHop.ip_address.to_string()); + ip_refcnt = vtep_ptr->getRemoteEndPointIPRefCnt(nextHop.ip_address.to_string()); } SWSS_LOG_INFO("Routeorch Del Remote VTEP %s, VNI %d, VR_ID %" PRIx64 ", IP ref_cnt %d", diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index e7e6f84bbb..3a7dec7132 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -906,7 +906,11 @@ bool VxlanTunnel::createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, void VxlanTunnel::deletePendingSIPTunnel() { - if ((getDipTunnelCnt() == 0) && del_tnl_hw_pending) + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + bool dip_tunnels_used = tunnel_orch->dipTunnelsUsed(); + + if (((dip_tunnels_used && getDipTunnelCnt() == 0) || + !dip_tunnels_used ) && del_tnl_hw_pending) { uint8_t mapper_list=0; @@ -994,7 +998,7 @@ int VxlanTunnel::getDipTunnelIMRRefCnt(const std::string remote_vtep) } } -int VxlanTunnel::getDipTunnelIPRefCnt(const std::string remote_vtep) +int VxlanTunnel::getRemoteEndPointIPRefCnt(const std::string remote_vtep) { tunnel_refcnt_t tnl_refcnts; @@ -1058,6 +1062,43 @@ void VxlanTunnel::updateDipTunnelRefCnt(bool inc, tunnel_refcnt_t& tnl_refcnts, } } +void VxlanTunnel::updateRemoteEndPointIpRef(const std::string remote_vtep, bool inc) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (inc) + { + if (it == tnl_users_.end()) + { + memset(&tnl_refcnts,0,sizeof(tunnel_refcnt_t)); + tnl_refcnts.ip_refcnt++; + tnl_users_[remote_vtep] = tnl_refcnts; + } + else + { + it->second.ip_refcnt++; + } + SWSS_LOG_DEBUG("Incrementing remote end point %s reference to %d", remote_vtep.c_str(), + it->second.ip_refcnt); + } + else + { + if (it == tnl_users_.end()) + { + SWSS_LOG_ERROR("Cannot decrement ref. End point not referenced %s", remote_vtep.c_str()); + } + it->second.ip_refcnt--; + + SWSS_LOG_DEBUG("Decrementing remote end point %s reference to %d", remote_vtep.c_str(), + it->second.ip_refcnt); + if (it->second.ip_refcnt == 0) + { + tnl_users_.erase(remote_vtep); + } + } +} + bool VxlanTunnel::createDynamicDIPTunnel(const std::string dip, tunnel_user_t usr) { uint8_t mapper_list = 0; @@ -1439,6 +1480,12 @@ bool VxlanTunnelOrch::addTunnelUser(const std::string remote_vtep, uint32_t vni return false; } + if (!dipTunnelsUsed() && usr == TUNNEL_USER_IP) + { + vtep_ptr->updateRemoteEndPointIpRef(remote_vtep, true); + return true; + } + vtep_ptr->createDynamicDIPTunnel(remote_vtep, usr); getTunnelNameFromDIP(remote_vtep, tunnel_name); @@ -1479,12 +1526,29 @@ bool VxlanTunnelOrch::delTunnelUser(const std::string remote_vtep, uint32_t vni Port tunnelPort; gPortsOrch->getPort(port_tunnel_name,tunnelPort); + bool ret; + + if (!dipTunnelsUsed()) + { + vtep_ptr->updateRemoteEndPointIpRef(remote_vtep, false); + if (vtep_ptr->del_tnl_hw_pending && !vtep_ptr->isTunnelReferenced()) + { + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + port_tunnel_name.c_str(), tunnelPort.m_fdb_count); + return true; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } + return true; + } if ((vtep_ptr->getDipTunnelRefCnt(remote_vtep) == 1) && tunnelPort.m_fdb_count == 0) { - bool ret; - ret = gPortsOrch->removeBridgePort(tunnelPort); if (!ret) { @@ -1522,6 +1586,18 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) if (isSrcVtepTunnel(tunnelPort)) { + if (vtep_ptr->del_tnl_hw_pending && !vtep_ptr->isTunnelReferenced()) + { + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + tunnelPort.m_alias.c_str(), tunnelPort.m_fdb_count); + return; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } return; } getTunnelDIPFromPort(tunnelPort, remote_vtep); @@ -1677,6 +1753,49 @@ bool VxlanTunnelOrch::getTunnelPort(const std::string& vtep,Port& tunnelPort, bo return ret; } +bool VxlanTunnel::isTunnelReferenced() +{ + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + auto src_vtep = getSrcIP().to_string(); + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + bool ret; + Port tunnelPort; + bool dip_tunnels_used = tunnel_orch->dipTunnelsUsed(); + + ret = gPortsOrch->getPort(port_tunnel_name, tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); + return false; + } + + if (tunnelPort.m_fdb_count != 0) + { + return true; + } + + if (dip_tunnels_used) + { + return (getDipTunnelCnt() != 0); + } + else + { + /* Bridge port will have reference since on IMET routes reception L2MC group member + would be created with end point IP and the P2MP tunnel bridge port */ + + if (gPortsOrch->getBridgePortReferenceCount(tunnelPort) != 0) + { + return true; + } + /* If there are routes pointing to the tunnel */ + if (!tnl_users_.empty()) + { + return true; + } + } + return false; +} + //------------------- VXLAN_TUNNEL_MAP Table --------------------------// bool VxlanTunnelMapOrch::addOperation(const Request& request) @@ -1785,7 +1904,7 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) const auto& tunnel_name = request.getKeyString(0); const auto& tunnel_map_entry_name = request.getKeyString(1); const auto& full_tunnel_map_entry_name = request.getFullKey(); - + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!isTunnelMapExists(full_tunnel_map_entry_name)) { @@ -1815,7 +1934,6 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) vxlan_tunnel_map_table_.erase(full_tunnel_map_entry_name); - VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!tunnel_orch->isTunnelExists(tunnel_name)) { SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); @@ -1832,22 +1950,21 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) if (tunnel_obj->vlan_vrf_vni_count == 0) { + Port tunnelPort; + auto src_vtep = tunnel_obj->getSrcIP().to_string(); + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + bool ret; + + ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); // If there are Dynamic DIP Tunnels referring to this SIP Tunnel // then mark it as pending for delete. - if (tunnel_obj->getDipTunnelCnt() == 0) + if (!tunnel_obj->isTunnelReferenced()) { - Port tunnelPort; - auto src_vtep = tunnel_obj->getSrcIP().to_string(); - auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); - bool ret; - - ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); if (!ret) { SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); return true; } - ret = gPortsOrch->removeBridgePort(tunnelPort); if (!ret) { @@ -1866,8 +1983,16 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) else { tunnel_obj->del_tnl_hw_pending = true; - SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion DIP Tunnel count = %d", - tunnel_obj->getDipTunnelCnt()); + if (tunnel_orch->dipTunnelsUsed()) + { + SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion DIP Tunnel count = %d", + tunnel_obj->getDipTunnelCnt()); + } + else + { + SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion Remote reference count = %d", + gPortsOrch->getBridgePortReferenceCount(tunnelPort)); + } } } @@ -2297,6 +2422,20 @@ bool EvpnRemoteVnip2mpOrch::delOperation(const Request& request) return false; } + if (vtep_ptr->del_tnl_hw_pending && + !vtep_ptr->isTunnelReferenced()) + { + bool ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + src_vtep.c_str(), tunnelPort.m_fdb_count); + return true; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } + return true; } diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index c939fd4aa3..c8a8cabd4f 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -194,11 +194,13 @@ class VxlanTunnel // Total Routes using the DIP tunnel. int getDipTunnelRefCnt(const std::string); int getDipTunnelIMRRefCnt(const std::string); - int getDipTunnelIPRefCnt(const std::string); + int getRemoteEndPointIPRefCnt(const std::string); // Total DIP tunnels associated with this SIP tunnel. int getDipTunnelCnt(); bool createDynamicDIPTunnel(const string dip, tunnel_user_t usr); bool deleteDynamicDIPTunnel(const string dip, tunnel_user_t usr, bool update_refcnt = true); + bool isTunnelReferenced(void); + void updateRemoteEndPointIpRef(const std::string remote_vtep, bool inc); uint32_t vlan_vrf_vni_count = 0; bool del_tnl_hw_pending = false; diff --git a/tests/test_evpn_p2p_tunnel.py b/tests/test_evpn_p2p_tunnel.py deleted file mode 100644 index c8a150595f..0000000000 --- a/tests/test_evpn_p2p_tunnel.py +++ /dev/null @@ -1,690 +0,0 @@ -from swsscommon import swsscommon -import time -import json -import random -import pytest -from pprint import pprint - - -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) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_entry_pst(db, table, separator, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - - -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - -def get_exist_entries(dvs, table): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_created_entry(db, table, existed_entries): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == 1, "Wrong number of created entries." - return new_entries[0] - -def get_created_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." - new_entries.sort() - return new_entries - -def get_default_vr_id(dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] - - -def check_object(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key in keys, "The desired key is not presented" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - for name, value in fvs: - if name in expected_attributes: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - -def get_key_with_attr(db, table, expected_attributes ): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - retkey = list() - #assert key in keys, "The desired key is not presented" - - for key in keys: - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - num_match = 0 - for name, value in fvs: - if name in expected_attributes: - if expected_attributes[name] == value: - num_match += 1 - if num_match == len(expected_attributes): - retkey.append(key) - - return retkey - - -def create_evpn_nvo(dvs, nvoname, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("source_vtep", tnl_name), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_EVPN_NVO", '|', nvoname, - attrs, - ) - -def remove_evpn_nvo(dvs, nvoname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - if not skip_dst_ip: - attrs.append(("dst_ip", dst_ip)) - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - -def create_vxlan_tunnel_map(dvs, tnl_name, map_name, vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnl_name, map_name), - attrs, - ) - -def create_evpn_remote_vni(dvs, vlan_id, remote_vtep, vnid): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remote_vtep), - [ - ("vni", vnid), - ], - ) - time.sleep(2) - -def remove_vxlan_tunnel(dvs, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL", tnl_name, - ) - -def remove_vxlan_tunnel_map(dvs, tnl_name, map_name,vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), - ) - -def remove_evpn_remote_vni(dvs, vlan_id, remote_vtep ): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), - ) - time.sleep(2) - -def create_vlan1(dvs, vlan_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - -loopback_id = 0 -def_vr_id = 0 -switch_mac = None - -class VxlanTunnel(object): - - ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" - ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" - ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" - ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" - ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" - - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - bridgeport_ids = set() - tunnel_map_map = {} - tunnel = {} - tunnel_appdb = {} - tunnel_term = {} - map_entry_map = {} - dip_tunnel_map = {} - dip_tun_state_map = {} - bridgeport_map = {} - vlan_id_map = {} - vlan_member_map = {} - - def fetch_exist_entries(self, dvs): - self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) - self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) - self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) - self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) - self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) - - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - for x in range(len(vidlist)): - status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) - assert status == False, "SIP Tunnel Map entry not deleted" - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode != 0, "Kernel device not deleted" - - def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) - assert len(ret) > 0, "SIP TunnelMap entry not created" - assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" - self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode == 0, "Kernel device not created" - - - def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") - status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from APP_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) - status, fvs = tbl.get(self.tunnel_term[tunnel_name]) - assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.tunnel[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) - assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) - assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) - assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) - assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" - - def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) - tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - } - ) - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, - - check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, - { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - } - ) - - expected_attributes = { - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, - } - - if not skip_dst_ip: - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' - - check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) - - expected_siptnl_attributes = { - 'src_ip': src_ip, - } - - if not skip_dst_ip: - expected_siptnl_attributes['dst_ip'] = dst_ip - - ret = get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) - assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.tunnel_appdb[tunnel_name] = ret[0] - - self.tunnel_map_ids.update(tunnel_map_id) - self.tunnel_ids.add(tunnel_id) - self.tunnel_term_ids.add(tunnel_term_id) - self.tunnel_map_map[tunnel_name] = tunnel_map_id - self.tunnel[tunnel_name] = tunnel_id - self.tunnel_term[tunnel_name] = tunnel_term_id - - def check_vxlan_dip_tunnel_delete(self, dvs, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') - status, fvs = tbl.get(self.dip_tun_state_map[dip]) - assert status == False, "State Table entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.dip_tunnel_map[dip]) - assert status == False, "Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) - status, fvs = tbl.get(self.bridgeport_map[dip]) - assert status == False, "Tunnel bridgeport entry not deleted" - - def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - expected_state_attributes = { - 'src_ip': src_ip, - 'dst_ip': dip, - 'tnl_src': 'EVPN', - } - - ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) - assert len(ret) > 0, "Tunnel Statetable entry not created" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.dip_tun_state_map[dip] = ret[0] - - - tunnel_map_id = self.tunnel_map_map[vtep_name] - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - print(decapstr) - print(encapstr) - - expected_tun_attributes = { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, - } - - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) - assert len(ret) > 0, "Tunnel entry not created" - assert len(ret) == 1, "More than 1 tunnel entry created" - - self.dip_tunnel_map[dip] = ret[0] - tunnel_id = ret[0] - - expected_bridgeport_attributes = { - 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', - 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, - 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', - 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', - } - - ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) - assert len(ret) > 0, "Bridgeport entry not created" - assert len(ret) == 1, "More than 1 bridgeport entry created" - - self.bridgeport_map[dip] = ret[0] - - def check_vlan_extension_delete(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') - status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) - assert status == False, "VLAN Member entry not deleted" - - def check_vlan_extension(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - expected_vlan_attributes = { - 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) - assert len(ret) > 0, "VLAN entry not created" - assert len(ret) == 1, "More than 1 VLAN entry created" - - self.vlan_id_map[vlan_name] = ret[0] - - expected_vlan_member_attributes = { - 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], - 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) - assert len(ret) > 0, "VLAN Member not created" - assert len(ret) == 1, "More than 1 VLAN member created" - self.vlan_member_map[dip+vlan_name] = ret[0] - - def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - time.sleep(2) - - if (self.tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) - else: - tunnel_map_id = self.tunnel_map_map[tunnel_name] - - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), - } - ) - - self.tunnel_map_entry_ids.update(tunnel_map_entry_id) - -class TestVxlanOrch(object): - - def get_vxlan_obj(self): - return VxlanTunnel() - -# Test 1 - Create and Delete SIP Tunnel and Map entries - def test_p2mp_tunnel(self, dvs, testlog): - vxlan_obj = self.get_vxlan_obj() - - tunnel_name = 'tunnel_1' - map_name = 'map_1000_100' - map_name_1 = 'map_1001_101' - map_name_2 = 'map_1002_102' - - vxlan_obj.fetch_exist_entries(dvs) - - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - - vlanlist = ['100', '101', '102'] - vnilist = ['1000', '1001', '1002'] - - print("Testing SIP Tunnel Creation") - vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) - - print("Testing Tunnel Map Entry") - vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) - - print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) - - print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) - -# Test 2 - DIP Tunnel Tests - def test_p2p_tunnel(self, dvs, testlog): - vxlan_obj = self.get_vxlan_obj() - - tunnel_name = 'tunnel_2' - map_name = 'map_1000_100' - map_name_1 = 'map_1001_101' - map_name_2 = 'map_1002_102' - vlanlist = ['100', '101', '102'] - vnilist = ['1000', '1001', '1002'] - - vxlan_obj.fetch_exist_entries(dvs) - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - - vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) - vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) - - create_evpn_nvo(dvs, 'nvo1', tunnel_name) - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') - - print("Testing DIP tunnel creation") - vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') - print("Testing VLAN 100 extension") - vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') - - create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') - create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') - - print("Testing DIP tunnel not created again") - vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') - - print("Testing VLAN 101 extension") - vxlan_obj.check_vlan_extension(dvs, '101', '7.7.7.7') - - print("Testing VLAN 102 extension") - vxlan_obj.check_vlan_extension(dvs, '102', '7.7.7.7') - - print("Testing another DIP tunnel to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') - print("Testing DIP tunnel creation to 8.8.8.8") - vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') - print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") - vxlan_obj.check_vlan_extension(dvs, '100', '8.8.8.8') - vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') - - print("Testing Vlan Extension removal") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') - remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') - vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') - vxlan_obj.check_vlan_extension_delete(dvs, '101', '7.7.7.7') - print("Testing DIP tunnel not deleted") - vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') - - print("Testing Last Vlan removal and DIP tunnel delete") - remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') - vxlan_obj.check_vlan_extension_delete(dvs, '102', '7.7.7.7') - vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') - - print("Testing Last Vlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') - vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') - vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') - - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) - - print("Testing SIP Tunnel Deletion") - remove_evpn_nvo(dvs, 'nvo1') - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) - - -# Test 3 - Create and Delete SIP Tunnel and Map entries - def test_p2mp_tunnel_with_dip(self, dvs, testlog): - vxlan_obj = self.get_vxlan_obj() - - tunnel_name = 'tunnel_2' - map_name = 'map_1000_100' - map_name_1 = 'map_1001_101' - map_name_2 = 'map_1002_102' - - vxlan_obj.fetch_exist_entries(dvs) - - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - - vlanlist = ['100', '101', '102'] - vnilist = ['1000', '1001', '1002'] - - print("Testing SIP Tunnel Creation") - vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist, '2.2.2.2', False) - - print("Testing Tunnel Map Entry") - vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) - - print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') - vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) - - print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) From c81e3195ada04c0a0093621a8d3a3a236750876f Mon Sep 17 00:00:00 2001 From: haslersn Date: Wed, 21 Jul 2021 05:06:14 +0200 Subject: [PATCH 15/28] refactor(fdbsyncd): Convert files with dos2unix (#1828) *dos2unix fdbsyncd/* Signed-off-by: Sebastian Hasler --- fdbsyncd/Makefile.am | 32 +- fdbsyncd/fdbsync.cpp | 1442 ++++++++++++++++++++--------------------- fdbsyncd/fdbsync.h | 290 ++++----- fdbsyncd/fdbsyncd.cpp | 312 ++++----- 4 files changed, 1038 insertions(+), 1038 deletions(-) diff --git a/fdbsyncd/Makefile.am b/fdbsyncd/Makefile.am index 0ad7d67df4..06beefaf22 100644 --- a/fdbsyncd/Makefile.am +++ b/fdbsyncd/Makefile.am @@ -1,16 +1,16 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart - -bin_PROGRAMS = fdbsyncd - -if DEBUG -DBGFLAGS = -ggdb -DDEBUG -else -DBGFLAGS = -g -endif - -fdbsyncd_SOURCES = fdbsyncd.cpp fdbsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp - -fdbsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) -fdbsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) -fdbsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) - +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart + +bin_PROGRAMS = fdbsyncd + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +fdbsyncd_SOURCES = fdbsyncd.cpp fdbsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp + +fdbsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) +fdbsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) +fdbsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) + diff --git a/fdbsyncd/fdbsync.cpp b/fdbsyncd/fdbsync.cpp index 30c9e2d54c..5a5caf5a89 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -1,721 +1,721 @@ -#include -#include -#include -#include -#include -#include - -#include "logger.h" -#include "dbconnector.h" -#include "producerstatetable.h" -#include "ipaddress.h" -#include "netmsg.h" -#include "macaddress.h" -#include "exec.h" -#include "fdbsync.h" -#include "warm_restart.h" -#include "errno.h" - -using namespace std; -using namespace swss; - -#define VXLAN_BR_IF_NAME_PREFIX "Brvxlan" - -FdbSync::FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db) : - m_fdbTable(pipelineAppDB, APP_VXLAN_FDB_TABLE_NAME), - m_imetTable(pipelineAppDB, APP_VXLAN_REMOTE_VNI_TABLE_NAME), - m_fdbStateTable(stateDb, STATE_FDB_TABLE_NAME), - m_cfgEvpnNvoTable(config_db, CFG_VXLAN_EVPN_NVO_TABLE_NAME) -{ - m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "fdbsyncd", "swss", DEFAULT_FDBSYNC_WARMSTART_TIMER); - if (m_AppRestartAssist) - { - m_AppRestartAssist->registerAppTable(APP_VXLAN_FDB_TABLE_NAME, &m_fdbTable); - m_AppRestartAssist->registerAppTable(APP_VXLAN_REMOTE_VNI_TABLE_NAME, &m_imetTable); - } -} - -FdbSync::~FdbSync() -{ - if (m_AppRestartAssist) - { - delete m_AppRestartAssist; - } -} - - -// 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; - m_cfgEvpnNvoTable.pops(entries); - bool lastNvoState = m_isEvpnNvoExist; - - for (auto entry: entries) - { - std::string op = kfvOp(entry); - - if (op == SET_COMMAND) - { - m_isEvpnNvoExist = true; - } - else if (op == DEL_COMMAND) - { - m_isEvpnNvoExist = false; - } - - if (lastNvoState != m_isEvpnNvoExist) - { - updateAllLocalMac(); - } - } - return; -} - -void FdbSync::updateAllLocalMac() -{ - for ( auto it = m_fdb_mac.begin(); it != m_fdb_mac.end(); ++it ) - { - if (m_isEvpnNvoExist) - { - /* Add the Local FDB entry into Kernel */ - addLocalMac(it->first, "replace"); - } - else - { - /* Delete the Local FDB entry from Kernel */ - addLocalMac(it->first, "del"); - } - } -} - -void FdbSync::processStateFdb() -{ - struct m_fdb_info info; - std::deque entries; - - m_fdbStateTable.pops(entries); - - int count =0 ; - for (auto entry: entries) - { - count++; - std::string key = kfvKey(entry); - std::string op = kfvOp(entry); - - std::size_t delimiter = key.find_first_of(":"); - auto vlan_name = key.substr(0, delimiter); - auto mac_address = key.substr(delimiter+1); - - info.vid = vlan_name; - info.mac = mac_address; - - if(op == "SET") - { - info.op_type = FDB_OPER_ADD ; - } - else - { - info.op_type = FDB_OPER_DEL ; - } - - SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); - - for (auto i : kfvFieldsValues(entry)) - { - SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " - "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); - - if(fvField(i) == "port") - { - info.port_name = fvValue(i); - } - - if(fvField(i) == "type") - { - if(fvValue(i) == "dynamic") - { - info.type = FDB_TYPE_DYNAMIC; - } - else if (fvValue(i) == "static") - { - info.type = FDB_TYPE_STATIC; - } - } - } - - if (op != "SET" && macCheckSrcDB(&info) == false) - { - continue; - } - updateLocalMac(&info); - } -} - -void FdbSync::macUpdateCache(struct m_fdb_info *info) -{ - string key = info->vid + ":" + info->mac; - m_fdb_mac[key].port_name = info->port_name; - m_fdb_mac[key].type = info->type; - - return; -} - -bool FdbSync::macCheckSrcDB(struct m_fdb_info *info) -{ - string key = info->vid + ":" + info->mac; - if (m_fdb_mac.find(key) != m_fdb_mac.end()) - { - SWSS_LOG_INFO("DEL_KEY %s ", key.c_str()); - return true; - } - - return false; -} - -void FdbSync::macDelVxlanEntry(string auxkey, struct m_fdb_info *info) -{ - std::string vtep = m_mac[auxkey].vtep; - - const std::string cmds = std::string("") - + " bridge fdb del " + info->mac + " dev " - + m_mac[auxkey].ifname + " dst " + vtep + " vlan " + info->vid.substr(4); - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Success cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - - return; -} - -void FdbSync::updateLocalMac (struct m_fdb_info *info) -{ - char *op; - char *type; - string port_name = ""; - string key = info->vid + ":" + info->mac; - short fdb_type; /*dynamic or static*/ - - if (info->op_type == FDB_OPER_ADD) - { - macUpdateCache(info); - op = "replace"; - port_name = info->port_name; - fdb_type = info->type; - /* Check if this vlan+key is also learned by vxlan neighbor then delete learned on */ - if (m_mac.find(key) != m_mac.end()) - { - macDelVxlanEntry(key, info); - SWSS_LOG_INFO("Local learn event deleting from VXLAN table DEL_KEY %s", key.c_str()); - macDelVxlan(key); - } - } - else - { - op = "del"; - port_name = m_fdb_mac[key].port_name; - fdb_type = m_fdb_mac[key].type; - m_fdb_mac.erase(key); - } - - if (!m_isEvpnNvoExist) - { - SWSS_LOG_INFO("Ignore kernel update EVPN NVO is not configured MAC %s", key.c_str()); - return; - } - - if (fdb_type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + op + " " + info->mac + " dev " - + port_name + " master " + type + " vlan " + info->vid.substr(4); - - std::string res; - int ret = swss::exec(cmds, res); - - SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - - return; -} - -void FdbSync::addLocalMac(string key, string op) -{ - char *type; - string port_name = ""; - string mac = ""; - string vlan = ""; - size_t str_loc = string::npos; - - str_loc = key.find(":"); - if (str_loc == string::npos) - { - SWSS_LOG_ERROR("Local MAC issue with Key:%s", key.c_str()); - return; - } - vlan = key.substr(4, str_loc-4); - mac = key.substr(str_loc+1, std::string::npos); - - SWSS_LOG_INFO("Local route Vlan:%s MAC:%s Key:%s Op:%s", vlan.c_str(), mac.c_str(), key.c_str(), op.c_str()); - - if (m_fdb_mac.find(key)!=m_fdb_mac.end()) - { - port_name = m_fdb_mac[key].port_name; - if (port_name.empty()) - { - SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); - return; - } - - if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + op + " " + mac + " dev " - + port_name + " master " + type + " vlan " + vlan; - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Config triggered cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - return; -} - -/* - * This is a special case handling where mac is learned in the ASIC. - * Then MAC is learned in the Kernel, Since this mac is learned in the Kernel - * This MAC will age out, when MAC delete is received from the Kernel. - * If MAC is still present in the state DB cache then fdbsyncd will be - * re-programmed with MAC in the Kernel - */ -void FdbSync::macRefreshStateDB(int vlan, string kmac) -{ - string key = "Vlan" + to_string(vlan) + ":" + kmac; - char *type; - string port_name = ""; - - SWSS_LOG_INFO("Refreshing Vlan:%d MAC route MAC:%s Key %s", vlan, kmac.c_str(), key.c_str()); - - if (m_fdb_mac.find(key)!=m_fdb_mac.end()) - { - port_name = m_fdb_mac[key].port_name; - if (port_name.empty()) - { - SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); - return; - } - - if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + "replace" + " " + kmac + " dev " - + port_name + " master " + type + " vlan " + to_string(vlan); - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Refreshing cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - return; -} - -bool FdbSync::checkImetExist(string key, uint32_t vni) -{ - if (m_imet_route.find(key) != m_imet_route.end()) - { - SWSS_LOG_INFO("IMET exist key:%s Vni:%d", key.c_str(), vni); - return false; - } - m_imet_route[key].vni = vni; - return true; -} - -bool FdbSync::checkDelImet(string key, uint32_t vni) -{ - int ret = false; - - SWSS_LOG_INFO("Del IMET key:%s Vni:%d", key.c_str(), vni); - if (m_imet_route.find(key) != m_imet_route.end()) - { - ret = true; - m_imet_route.erase(key); - } - return ret; -} - -void FdbSync::imetAddRoute(struct in_addr vtep, string vlan_str, uint32_t vni) -{ - string vlan_id = "Vlan" + vlan_str; - string key = vlan_id + ":" + inet_ntoa(vtep); - - if (!checkImetExist(key, vni)) - { - return; - } - - SWSS_LOG_INFO("%sIMET Add route key:%s vtep:%s %s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", - key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); - - std::vector fvVector; - FieldValueTuple f("vni", to_string(vni)); - fvVector.push_back(f); - - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, false); - return; - } - - m_imetTable.set(key, fvVector); - return; -} - -void FdbSync::imetDelRoute(struct in_addr vtep, string vlan_str, uint32_t vni) -{ - string vlan_id = "Vlan" + vlan_str; - string key = vlan_id + ":" + inet_ntoa(vtep); - - if (!checkDelImet(key, vni)) - { - return; - } - - SWSS_LOG_INFO("%sIMET Del route key:%s vtep:%s %s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", - key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); - - std::vector fvVector; - FieldValueTuple f("vni", to_string(vni)); - fvVector.push_back(f); - - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, true); - return; - } - - m_imetTable.del(key); - return; -} - -void FdbSync::macDelVxlanDB(string key) -{ - string vtep = m_mac[key].vtep; - string type; - string vni = to_string(m_mac[key].vni); - type = m_mac[key].type; - - std::vector fvVector; - FieldValueTuple rv("remote_vtep", vtep); - FieldValueTuple t("type", type); - FieldValueTuple v("vni", vni); - fvVector.push_back(rv); - 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()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, true); - return; - } - - m_fdbTable.del(key); - return; - -} - -void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t vni, string intf_name) -{ - string svtep = inet_ntoa(vtep); - string svni = to_string(vni); - - /* Update the DB with Vxlan MAC */ - m_mac[key] = {svtep, type, vni, intf_name}; - - std::vector fvVector; - FieldValueTuple rv("remote_vtep", svtep); - FieldValueTuple t("type", type); - FieldValueTuple v("vni", svni); - fvVector.push_back(rv); - 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()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, false); - return; - } - - m_fdbTable.set(key, fvVector); - - return; -} - -void FdbSync::macDelVxlan(string key) -{ - if (m_mac.find(key) != m_mac.end()) - { - SWSS_LOG_INFO("DEL_KEY %s vtep:%s type:%s", key.c_str(), m_mac[key].vtep.c_str(), m_mac[key].type.c_str()); - macDelVxlanDB(key); - m_mac.erase(key); - } - return; -} - -void FdbSync::onMsgNbr(int nlmsg_type, struct nl_object *obj) -{ - char macStr[MAX_ADDR_SIZE + 1] = {0}; - struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; - struct in_addr vtep = {0}; - int vlan = 0, ifindex = 0; - uint32_t vni = 0; - nl_addr *vtep_addr; - string ifname; - string key; - bool delete_key = false; - size_t str_loc = string::npos; - string type = ""; - string vlan_id = ""; - bool isVxlanIntf = false; - - if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && - (nlmsg_type != RTM_DELNEIGH)) - { - return; - } - - /* Only MAC route is to be supported */ - if (rtnl_neigh_get_family(neigh) != AF_BRIDGE) - { - return; - } - ifindex = rtnl_neigh_get_ifindex(neigh); - if (m_intf_info.find(ifindex) != m_intf_info.end()) - { - isVxlanIntf = true; - ifname = m_intf_info[ifindex].ifname; - } - - nl_addr2str(rtnl_neigh_get_lladdr(neigh), macStr, MAX_ADDR_SIZE); - - if (isVxlanIntf == false) - { - if (nlmsg_type != RTM_DELNEIGH) - { - return; - } - } - else - { - /* If this is for vnet bridge vxlan interface, then return */ - if (ifname.find(VXLAN_BR_IF_NAME_PREFIX) != string::npos) - { - return; - } - - /* VxLan netdevice should be in - format */ - str_loc = ifname.rfind("-"); - if (str_loc == string::npos) - { - return; - } - - vlan_id = "Vlan" + ifname.substr(str_loc+1, std::string::npos); - vni = m_intf_info[ifindex].vni; - } - - - if (isVxlanIntf == false) - { - vlan = rtnl_neigh_get_vlan(neigh); - if (m_isEvpnNvoExist) - { - macRefreshStateDB(vlan, macStr); - } - return; - } - - vtep_addr = rtnl_neigh_get_dst(neigh); - if (vtep_addr == NULL) - { - return; - } - else - { - /* Currently we only support ipv4 tunnel endpoints */ - vtep.s_addr = *(uint32_t *)nl_addr_get_binary_addr(vtep_addr); - SWSS_LOG_INFO("Tunnel IP %s Int%d", inet_ntoa(vtep), *(uint32_t *)nl_addr_get_binary_addr(vtep_addr)); - } - - int state = rtnl_neigh_get_state(neigh); - if ((nlmsg_type == RTM_DELNEIGH) || (state == NUD_INCOMPLETE) || - (state == NUD_FAILED)) - { - delete_key = true; - } - - if (state & NUD_NOARP) - { - /* This is a static route */ - type = "static"; - } - else - { - type = "dynamic"; - } - - /* Handling IMET routes */ - if (MacAddress(macStr) == MacAddress("00:00:00:00:00:00")) - { - if (vtep.s_addr) - { - string vlan_str = ifname.substr(str_loc+1, string::npos); - - if (!delete_key) - { - imetAddRoute(vtep, vlan_str, vni); - } - else - { - imetDelRoute(vtep, vlan_str, vni); - } - } - return; - } - - key+= vlan_id; - key+= ":"; - key+= macStr; - - if (!delete_key) - { - macAddVxlan(key, vtep, type, vni, ifname); - } - else - { - macDelVxlan(key); - } - return; -} - -void FdbSync::onMsgLink(int nlmsg_type, struct nl_object *obj) -{ - struct rtnl_link *link; - char *ifname = NULL; - char *nil = "NULL"; - int ifindex; - unsigned int vni; - - link = (struct rtnl_link *)obj; - ifname = rtnl_link_get_name(link); - ifindex = rtnl_link_get_ifindex(link); - if (rtnl_link_is_vxlan(link) == 0) - { - return; - } - - if (rtnl_link_vxlan_get_id(link, &vni) != 0) - { - SWSS_LOG_INFO("Op:%d VxLAN dev:%s index:%d vni:%d. Not found", nlmsg_type, ifname? ifname: nil, ifindex, vni); - return; - } - SWSS_LOG_INFO("Op:%d VxLAN dev %s index:%d vni:%d", nlmsg_type, ifname? ifname: nil, ifindex, vni); - if (nlmsg_type == RTM_NEWLINK) - { - m_intf_info[ifindex].vni = vni; - m_intf_info[ifindex].ifname = ifname; - } - return; -} - -void FdbSync::onMsg(int nlmsg_type, struct nl_object *obj) -{ - if ((nlmsg_type != RTM_NEWLINK) && - (nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_DELNEIGH)) - { - SWSS_LOG_DEBUG("netlink: unhandled event: %d", nlmsg_type); - return; - } - if (nlmsg_type == RTM_NEWLINK) - { - onMsgLink(nlmsg_type, obj); - } - else - { - onMsgNbr(nlmsg_type, obj); - } -} - +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "ipaddress.h" +#include "netmsg.h" +#include "macaddress.h" +#include "exec.h" +#include "fdbsync.h" +#include "warm_restart.h" +#include "errno.h" + +using namespace std; +using namespace swss; + +#define VXLAN_BR_IF_NAME_PREFIX "Brvxlan" + +FdbSync::FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db) : + m_fdbTable(pipelineAppDB, APP_VXLAN_FDB_TABLE_NAME), + m_imetTable(pipelineAppDB, APP_VXLAN_REMOTE_VNI_TABLE_NAME), + m_fdbStateTable(stateDb, STATE_FDB_TABLE_NAME), + m_cfgEvpnNvoTable(config_db, CFG_VXLAN_EVPN_NVO_TABLE_NAME) +{ + m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "fdbsyncd", "swss", DEFAULT_FDBSYNC_WARMSTART_TIMER); + if (m_AppRestartAssist) + { + m_AppRestartAssist->registerAppTable(APP_VXLAN_FDB_TABLE_NAME, &m_fdbTable); + m_AppRestartAssist->registerAppTable(APP_VXLAN_REMOTE_VNI_TABLE_NAME, &m_imetTable); + } +} + +FdbSync::~FdbSync() +{ + if (m_AppRestartAssist) + { + delete m_AppRestartAssist; + } +} + + +// 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; + m_cfgEvpnNvoTable.pops(entries); + bool lastNvoState = m_isEvpnNvoExist; + + for (auto entry: entries) + { + std::string op = kfvOp(entry); + + if (op == SET_COMMAND) + { + m_isEvpnNvoExist = true; + } + else if (op == DEL_COMMAND) + { + m_isEvpnNvoExist = false; + } + + if (lastNvoState != m_isEvpnNvoExist) + { + updateAllLocalMac(); + } + } + return; +} + +void FdbSync::updateAllLocalMac() +{ + for ( auto it = m_fdb_mac.begin(); it != m_fdb_mac.end(); ++it ) + { + if (m_isEvpnNvoExist) + { + /* Add the Local FDB entry into Kernel */ + addLocalMac(it->first, "replace"); + } + else + { + /* Delete the Local FDB entry from Kernel */ + addLocalMac(it->first, "del"); + } + } +} + +void FdbSync::processStateFdb() +{ + struct m_fdb_info info; + std::deque entries; + + m_fdbStateTable.pops(entries); + + int count =0 ; + for (auto entry: entries) + { + count++; + std::string key = kfvKey(entry); + std::string op = kfvOp(entry); + + std::size_t delimiter = key.find_first_of(":"); + auto vlan_name = key.substr(0, delimiter); + auto mac_address = key.substr(delimiter+1); + + info.vid = vlan_name; + info.mac = mac_address; + + if(op == "SET") + { + info.op_type = FDB_OPER_ADD ; + } + else + { + info.op_type = FDB_OPER_DEL ; + } + + SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); + + for (auto i : kfvFieldsValues(entry)) + { + SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " + "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); + + if(fvField(i) == "port") + { + info.port_name = fvValue(i); + } + + if(fvField(i) == "type") + { + if(fvValue(i) == "dynamic") + { + info.type = FDB_TYPE_DYNAMIC; + } + else if (fvValue(i) == "static") + { + info.type = FDB_TYPE_STATIC; + } + } + } + + if (op != "SET" && macCheckSrcDB(&info) == false) + { + continue; + } + updateLocalMac(&info); + } +} + +void FdbSync::macUpdateCache(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + m_fdb_mac[key].port_name = info->port_name; + m_fdb_mac[key].type = info->type; + + return; +} + +bool FdbSync::macCheckSrcDB(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + if (m_fdb_mac.find(key) != m_fdb_mac.end()) + { + SWSS_LOG_INFO("DEL_KEY %s ", key.c_str()); + return true; + } + + return false; +} + +void FdbSync::macDelVxlanEntry(string auxkey, struct m_fdb_info *info) +{ + std::string vtep = m_mac[auxkey].vtep; + + const std::string cmds = std::string("") + + " bridge fdb del " + info->mac + " dev " + + m_mac[auxkey].ifname + " dst " + vtep + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Success cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::updateLocalMac (struct m_fdb_info *info) +{ + char *op; + char *type; + string port_name = ""; + string key = info->vid + ":" + info->mac; + short fdb_type; /*dynamic or static*/ + + if (info->op_type == FDB_OPER_ADD) + { + macUpdateCache(info); + op = "replace"; + port_name = info->port_name; + fdb_type = info->type; + /* Check if this vlan+key is also learned by vxlan neighbor then delete learned on */ + if (m_mac.find(key) != m_mac.end()) + { + macDelVxlanEntry(key, info); + SWSS_LOG_INFO("Local learn event deleting from VXLAN table DEL_KEY %s", key.c_str()); + macDelVxlan(key); + } + } + else + { + op = "del"; + port_name = m_fdb_mac[key].port_name; + fdb_type = m_fdb_mac[key].type; + m_fdb_mac.erase(key); + } + + if (!m_isEvpnNvoExist) + { + SWSS_LOG_INFO("Ignore kernel update EVPN NVO is not configured MAC %s", key.c_str()); + return; + } + + if (fdb_type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + info->mac + " dev " + + port_name + " master " + type + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + + SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::addLocalMac(string key, string op) +{ + char *type; + string port_name = ""; + string mac = ""; + string vlan = ""; + size_t str_loc = string::npos; + + str_loc = key.find(":"); + if (str_loc == string::npos) + { + SWSS_LOG_ERROR("Local MAC issue with Key:%s", key.c_str()); + return; + } + vlan = key.substr(4, str_loc-4); + mac = key.substr(str_loc+1, std::string::npos); + + SWSS_LOG_INFO("Local route Vlan:%s MAC:%s Key:%s Op:%s", vlan.c_str(), mac.c_str(), key.c_str(), op.c_str()); + + if (m_fdb_mac.find(key)!=m_fdb_mac.end()) + { + port_name = m_fdb_mac[key].port_name; + if (port_name.empty()) + { + SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); + return; + } + + if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + mac + " dev " + + port_name + " master " + type + " vlan " + vlan; + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Config triggered cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + return; +} + +/* + * This is a special case handling where mac is learned in the ASIC. + * Then MAC is learned in the Kernel, Since this mac is learned in the Kernel + * This MAC will age out, when MAC delete is received from the Kernel. + * If MAC is still present in the state DB cache then fdbsyncd will be + * re-programmed with MAC in the Kernel + */ +void FdbSync::macRefreshStateDB(int vlan, string kmac) +{ + string key = "Vlan" + to_string(vlan) + ":" + kmac; + char *type; + string port_name = ""; + + SWSS_LOG_INFO("Refreshing Vlan:%d MAC route MAC:%s Key %s", vlan, kmac.c_str(), key.c_str()); + + if (m_fdb_mac.find(key)!=m_fdb_mac.end()) + { + port_name = m_fdb_mac[key].port_name; + if (port_name.empty()) + { + SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); + return; + } + + if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + "replace" + " " + kmac + " dev " + + port_name + " master " + type + " vlan " + to_string(vlan); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Refreshing cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + return; +} + +bool FdbSync::checkImetExist(string key, uint32_t vni) +{ + if (m_imet_route.find(key) != m_imet_route.end()) + { + SWSS_LOG_INFO("IMET exist key:%s Vni:%d", key.c_str(), vni); + return false; + } + m_imet_route[key].vni = vni; + return true; +} + +bool FdbSync::checkDelImet(string key, uint32_t vni) +{ + int ret = false; + + SWSS_LOG_INFO("Del IMET key:%s Vni:%d", key.c_str(), vni); + if (m_imet_route.find(key) != m_imet_route.end()) + { + ret = true; + m_imet_route.erase(key); + } + return ret; +} + +void FdbSync::imetAddRoute(struct in_addr vtep, string vlan_str, uint32_t vni) +{ + string vlan_id = "Vlan" + vlan_str; + string key = vlan_id + ":" + inet_ntoa(vtep); + + if (!checkImetExist(key, vni)) + { + return; + } + + SWSS_LOG_INFO("%sIMET Add route key:%s vtep:%s %s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", + key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); + + std::vector fvVector; + FieldValueTuple f("vni", to_string(vni)); + fvVector.push_back(f); + + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, false); + return; + } + + m_imetTable.set(key, fvVector); + return; +} + +void FdbSync::imetDelRoute(struct in_addr vtep, string vlan_str, uint32_t vni) +{ + string vlan_id = "Vlan" + vlan_str; + string key = vlan_id + ":" + inet_ntoa(vtep); + + if (!checkDelImet(key, vni)) + { + return; + } + + SWSS_LOG_INFO("%sIMET Del route key:%s vtep:%s %s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", + key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); + + std::vector fvVector; + FieldValueTuple f("vni", to_string(vni)); + fvVector.push_back(f); + + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, true); + return; + } + + m_imetTable.del(key); + return; +} + +void FdbSync::macDelVxlanDB(string key) +{ + string vtep = m_mac[key].vtep; + string type; + string vni = to_string(m_mac[key].vni); + type = m_mac[key].type; + + std::vector fvVector; + FieldValueTuple rv("remote_vtep", vtep); + FieldValueTuple t("type", type); + FieldValueTuple v("vni", vni); + fvVector.push_back(rv); + 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()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, true); + return; + } + + m_fdbTable.del(key); + return; + +} + +void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t vni, string intf_name) +{ + string svtep = inet_ntoa(vtep); + string svni = to_string(vni); + + /* Update the DB with Vxlan MAC */ + m_mac[key] = {svtep, type, vni, intf_name}; + + std::vector fvVector; + FieldValueTuple rv("remote_vtep", svtep); + FieldValueTuple t("type", type); + FieldValueTuple v("vni", svni); + fvVector.push_back(rv); + 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()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, false); + return; + } + + m_fdbTable.set(key, fvVector); + + return; +} + +void FdbSync::macDelVxlan(string key) +{ + if (m_mac.find(key) != m_mac.end()) + { + SWSS_LOG_INFO("DEL_KEY %s vtep:%s type:%s", key.c_str(), m_mac[key].vtep.c_str(), m_mac[key].type.c_str()); + macDelVxlanDB(key); + m_mac.erase(key); + } + return; +} + +void FdbSync::onMsgNbr(int nlmsg_type, struct nl_object *obj) +{ + char macStr[MAX_ADDR_SIZE + 1] = {0}; + struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; + struct in_addr vtep = {0}; + int vlan = 0, ifindex = 0; + uint32_t vni = 0; + nl_addr *vtep_addr; + string ifname; + string key; + bool delete_key = false; + size_t str_loc = string::npos; + string type = ""; + string vlan_id = ""; + bool isVxlanIntf = false; + + if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && + (nlmsg_type != RTM_DELNEIGH)) + { + return; + } + + /* Only MAC route is to be supported */ + if (rtnl_neigh_get_family(neigh) != AF_BRIDGE) + { + return; + } + ifindex = rtnl_neigh_get_ifindex(neigh); + if (m_intf_info.find(ifindex) != m_intf_info.end()) + { + isVxlanIntf = true; + ifname = m_intf_info[ifindex].ifname; + } + + nl_addr2str(rtnl_neigh_get_lladdr(neigh), macStr, MAX_ADDR_SIZE); + + if (isVxlanIntf == false) + { + if (nlmsg_type != RTM_DELNEIGH) + { + return; + } + } + else + { + /* If this is for vnet bridge vxlan interface, then return */ + if (ifname.find(VXLAN_BR_IF_NAME_PREFIX) != string::npos) + { + return; + } + + /* VxLan netdevice should be in - format */ + str_loc = ifname.rfind("-"); + if (str_loc == string::npos) + { + return; + } + + vlan_id = "Vlan" + ifname.substr(str_loc+1, std::string::npos); + vni = m_intf_info[ifindex].vni; + } + + + if (isVxlanIntf == false) + { + vlan = rtnl_neigh_get_vlan(neigh); + if (m_isEvpnNvoExist) + { + macRefreshStateDB(vlan, macStr); + } + return; + } + + vtep_addr = rtnl_neigh_get_dst(neigh); + if (vtep_addr == NULL) + { + return; + } + else + { + /* Currently we only support ipv4 tunnel endpoints */ + vtep.s_addr = *(uint32_t *)nl_addr_get_binary_addr(vtep_addr); + SWSS_LOG_INFO("Tunnel IP %s Int%d", inet_ntoa(vtep), *(uint32_t *)nl_addr_get_binary_addr(vtep_addr)); + } + + int state = rtnl_neigh_get_state(neigh); + if ((nlmsg_type == RTM_DELNEIGH) || (state == NUD_INCOMPLETE) || + (state == NUD_FAILED)) + { + delete_key = true; + } + + if (state & NUD_NOARP) + { + /* This is a static route */ + type = "static"; + } + else + { + type = "dynamic"; + } + + /* Handling IMET routes */ + if (MacAddress(macStr) == MacAddress("00:00:00:00:00:00")) + { + if (vtep.s_addr) + { + string vlan_str = ifname.substr(str_loc+1, string::npos); + + if (!delete_key) + { + imetAddRoute(vtep, vlan_str, vni); + } + else + { + imetDelRoute(vtep, vlan_str, vni); + } + } + return; + } + + key+= vlan_id; + key+= ":"; + key+= macStr; + + if (!delete_key) + { + macAddVxlan(key, vtep, type, vni, ifname); + } + else + { + macDelVxlan(key); + } + return; +} + +void FdbSync::onMsgLink(int nlmsg_type, struct nl_object *obj) +{ + struct rtnl_link *link; + char *ifname = NULL; + char *nil = "NULL"; + int ifindex; + unsigned int vni; + + link = (struct rtnl_link *)obj; + ifname = rtnl_link_get_name(link); + ifindex = rtnl_link_get_ifindex(link); + if (rtnl_link_is_vxlan(link) == 0) + { + return; + } + + if (rtnl_link_vxlan_get_id(link, &vni) != 0) + { + SWSS_LOG_INFO("Op:%d VxLAN dev:%s index:%d vni:%d. Not found", nlmsg_type, ifname? ifname: nil, ifindex, vni); + return; + } + SWSS_LOG_INFO("Op:%d VxLAN dev %s index:%d vni:%d", nlmsg_type, ifname? ifname: nil, ifindex, vni); + if (nlmsg_type == RTM_NEWLINK) + { + m_intf_info[ifindex].vni = vni; + m_intf_info[ifindex].ifname = ifname; + } + return; +} + +void FdbSync::onMsg(int nlmsg_type, struct nl_object *obj) +{ + if ((nlmsg_type != RTM_NEWLINK) && + (nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_DELNEIGH)) + { + SWSS_LOG_DEBUG("netlink: unhandled event: %d", nlmsg_type); + return; + } + if (nlmsg_type == RTM_NEWLINK) + { + onMsgLink(nlmsg_type, obj); + } + else + { + onMsgNbr(nlmsg_type, obj); + } +} + diff --git a/fdbsyncd/fdbsync.h b/fdbsyncd/fdbsync.h index ee6aa0845b..927b95dd0b 100644 --- a/fdbsyncd/fdbsync.h +++ b/fdbsyncd/fdbsync.h @@ -1,145 +1,145 @@ -#ifndef __FDBSYNC__ -#define __FDBSYNC__ - -#include -#include -#include "dbconnector.h" -#include "producerstatetable.h" -#include "subscriberstatetable.h" -#include "netmsg.h" -#include "warmRestartAssist.h" - -/* - * 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 { - -enum FDB_OP_TYPE { - FDB_OPER_ADD =1, - FDB_OPER_DEL = 2, -}; - -enum FDB_TYPE { - FDB_TYPE_STATIC = 1, - FDB_TYPE_DYNAMIC = 2, -}; - -struct m_fdb_info -{ - std::string mac; - std::string vid; /*Store as Vlan */ - std::string port_name; - short type; /*dynamic or static*/ - short op_type; /*add or del*/ -}; - -class FdbSync : public NetMsg -{ -public: - enum { MAX_ADDR_SIZE = 64 }; - - FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db); - ~FdbSync(); - - virtual void onMsg(int nlmsg_type, struct nl_object *obj); - - bool isIntfRestoreDone(); - - AppRestartAssist *getRestartAssist() - { - return m_AppRestartAssist; - } - - SubscriberStateTable *getFdbStateTable() - { - return &m_fdbStateTable; - } - - SubscriberStateTable *getCfgEvpnNvoTable() - { - return &m_cfgEvpnNvoTable; - } - - void processStateFdb(); - - void processCfgEvpnNvo(); - - bool m_reconcileDone = false; - - bool m_isEvpnNvoExist = false; - -private: - ProducerStateTable m_fdbTable; - ProducerStateTable m_imetTable; - SubscriberStateTable m_fdbStateTable; - AppRestartAssist *m_AppRestartAssist; - SubscriberStateTable m_cfgEvpnNvoTable; - - struct m_local_fdb_info - { - std::string port_name; - short type;/*dynamic or static*/ - }; - std::unordered_map m_fdb_mac; - - void macDelVxlanEntry(std::string auxkey, struct m_fdb_info *info); - - void macUpdateCache(struct m_fdb_info *info); - - bool macCheckSrcDB(struct m_fdb_info *info); - - void updateLocalMac(struct m_fdb_info *info); - - void updateAllLocalMac(); - - void macRefreshStateDB(int vlan, std::string kmac); - - bool checkImetExist(std::string key, uint32_t vni); - - bool checkDelImet(std::string key, uint32_t vni); - - struct m_mac_info - { - std::string vtep; - std::string type; - unsigned int vni; - std::string ifname; - }; - std::unordered_map m_mac; - - struct m_imet_info - { - unsigned int vni; - }; - std::unordered_map m_imet_route; - - struct intf - { - std::string ifname; - unsigned int vni; - }; - std::unordered_map m_intf_info; - - void addLocalMac(std::string key, std::string op); - void macAddVxlan(std::string key, struct in_addr vtep, std::string type, uint32_t vni, std::string intf_name); - void macDelVxlan(std::string auxkey); - void macDelVxlanDB(std::string key); - void imetAddRoute(struct in_addr vtep, std::string ifname, uint32_t vni); - void imetDelRoute(struct in_addr vtep, std::string ifname, uint32_t vni); - void onMsgNbr(int nlmsg_type, struct nl_object *obj); - void onMsgLink(int nlmsg_type, struct nl_object *obj); -}; - -} - -#endif - +#ifndef __FDBSYNC__ +#define __FDBSYNC__ + +#include +#include +#include "dbconnector.h" +#include "producerstatetable.h" +#include "subscriberstatetable.h" +#include "netmsg.h" +#include "warmRestartAssist.h" + +/* + * 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 { + +enum FDB_OP_TYPE { + FDB_OPER_ADD =1, + FDB_OPER_DEL = 2, +}; + +enum FDB_TYPE { + FDB_TYPE_STATIC = 1, + FDB_TYPE_DYNAMIC = 2, +}; + +struct m_fdb_info +{ + std::string mac; + std::string vid; /*Store as Vlan */ + std::string port_name; + short type; /*dynamic or static*/ + short op_type; /*add or del*/ +}; + +class FdbSync : public NetMsg +{ +public: + enum { MAX_ADDR_SIZE = 64 }; + + FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db); + ~FdbSync(); + + virtual void onMsg(int nlmsg_type, struct nl_object *obj); + + bool isIntfRestoreDone(); + + AppRestartAssist *getRestartAssist() + { + return m_AppRestartAssist; + } + + SubscriberStateTable *getFdbStateTable() + { + return &m_fdbStateTable; + } + + SubscriberStateTable *getCfgEvpnNvoTable() + { + return &m_cfgEvpnNvoTable; + } + + void processStateFdb(); + + void processCfgEvpnNvo(); + + bool m_reconcileDone = false; + + bool m_isEvpnNvoExist = false; + +private: + ProducerStateTable m_fdbTable; + ProducerStateTable m_imetTable; + SubscriberStateTable m_fdbStateTable; + AppRestartAssist *m_AppRestartAssist; + SubscriberStateTable m_cfgEvpnNvoTable; + + struct m_local_fdb_info + { + std::string port_name; + short type;/*dynamic or static*/ + }; + std::unordered_map m_fdb_mac; + + void macDelVxlanEntry(std::string auxkey, struct m_fdb_info *info); + + void macUpdateCache(struct m_fdb_info *info); + + bool macCheckSrcDB(struct m_fdb_info *info); + + void updateLocalMac(struct m_fdb_info *info); + + void updateAllLocalMac(); + + void macRefreshStateDB(int vlan, std::string kmac); + + bool checkImetExist(std::string key, uint32_t vni); + + bool checkDelImet(std::string key, uint32_t vni); + + struct m_mac_info + { + std::string vtep; + std::string type; + unsigned int vni; + std::string ifname; + }; + std::unordered_map m_mac; + + struct m_imet_info + { + unsigned int vni; + }; + std::unordered_map m_imet_route; + + struct intf + { + std::string ifname; + unsigned int vni; + }; + std::unordered_map m_intf_info; + + void addLocalMac(std::string key, std::string op); + void macAddVxlan(std::string key, struct in_addr vtep, std::string type, uint32_t vni, std::string intf_name); + void macDelVxlan(std::string auxkey); + void macDelVxlanDB(std::string key); + void imetAddRoute(struct in_addr vtep, std::string ifname, uint32_t vni); + void imetDelRoute(struct in_addr vtep, std::string ifname, uint32_t vni); + void onMsgNbr(int nlmsg_type, struct nl_object *obj); + void onMsgLink(int nlmsg_type, struct nl_object *obj); +}; + +} + +#endif + diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index dac4bb85e5..6b9724d89e 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -1,156 +1,156 @@ -#include -#include -#include -#include -#include "logger.h" -#include "select.h" -#include "netdispatcher.h" -#include "netlink.h" -#include "fdbsyncd/fdbsync.h" -#include "warm_restart.h" - -using namespace std; -using namespace swss; - -int main(int argc, char **argv) -{ - Logger::linkToDbNative("fdbsyncd"); - - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - RedisPipeline pipelineAppDB(&appDb); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector log_db(LOGLEVEL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - - FdbSync sync(&pipelineAppDB, &stateDb, &config_db); - - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); - - while (1) - { - try - { - NetLink netlink; - Selectable *temps; - int ret; - Select s; - SelectableTimer replayCheckTimer(timespec{0, 0}); - - using namespace std::chrono; - - /* - * If WarmStart is enabled, restore the VXLAN-FDB and VNI - * tables and start a reconcillation timer - */ - if (sync.getRestartAssist()->isWarmStartInProgress()) - { - sync.getRestartAssist()->readTablesToMap(); - - 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); - netlink.registerGroup(RTNLGRP_NEIGH); - SWSS_LOG_NOTICE("Listens to link and neigh messages..."); - netlink.dumpRequest(RTM_GETLINK); - s.addSelectable(&netlink); - ret = s.select(&temps, 1); - if (ret == Select::ERROR) - { - SWSS_LOG_ERROR("Error in RTM_GETLINK dump"); - } - - netlink.dumpRequest(RTM_GETNEIGH); - - s.addSelectable(sync.getFdbStateTable()); - s.addSelectable(sync.getCfgEvpnNvoTable()); - while (true) - { - s.select(&temps); - - if (temps == (Selectable *)sync.getFdbStateTable()) - { - sync.processStateFdb(); - } - else if (temps == (Selectable *)sync.getCfgEvpnNvoTable()) - { - 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 - { - /* - * If warmstart is in progress, we check the reconcile timer, - * if timer expired, we stop the timer and start the reconcile process - */ - if (sync.getRestartAssist()->isWarmStartInProgress()) - { - if (sync.getRestartAssist()->checkReconcileTimer(temps)) - { - sync.m_reconcileDone = true; - sync.getRestartAssist()->stopReconcileTimer(s); - sync.getRestartAssist()->reconcile(); - SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete"); - } - } - } - } - } - catch (const std::exception& e) - { - cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; - return 0; - } - } - - return 1; -} +#include +#include +#include +#include +#include "logger.h" +#include "select.h" +#include "netdispatcher.h" +#include "netlink.h" +#include "fdbsyncd/fdbsync.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("fdbsyncd"); + + DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + RedisPipeline pipelineAppDB(&appDb); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector log_db(LOGLEVEL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + FdbSync sync(&pipelineAppDB, &stateDb, &config_db); + + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); + + while (1) + { + try + { + NetLink netlink; + Selectable *temps; + int ret; + Select s; + SelectableTimer replayCheckTimer(timespec{0, 0}); + + using namespace std::chrono; + + /* + * If WarmStart is enabled, restore the VXLAN-FDB and VNI + * tables and start a reconcillation timer + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + sync.getRestartAssist()->readTablesToMap(); + + 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); + netlink.registerGroup(RTNLGRP_NEIGH); + SWSS_LOG_NOTICE("Listens to link and neigh messages..."); + netlink.dumpRequest(RTM_GETLINK); + s.addSelectable(&netlink); + ret = s.select(&temps, 1); + if (ret == Select::ERROR) + { + SWSS_LOG_ERROR("Error in RTM_GETLINK dump"); + } + + netlink.dumpRequest(RTM_GETNEIGH); + + s.addSelectable(sync.getFdbStateTable()); + s.addSelectable(sync.getCfgEvpnNvoTable()); + while (true) + { + s.select(&temps); + + if (temps == (Selectable *)sync.getFdbStateTable()) + { + sync.processStateFdb(); + } + else if (temps == (Selectable *)sync.getCfgEvpnNvoTable()) + { + 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 + { + /* + * If warmstart is in progress, we check the reconcile timer, + * if timer expired, we stop the timer and start the reconcile process + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + if (sync.getRestartAssist()->checkReconcileTimer(temps)) + { + sync.m_reconcileDone = true; + sync.getRestartAssist()->stopReconcileTimer(s); + sync.getRestartAssist()->reconcile(); + SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete"); + } + } + } + } + } + catch (const std::exception& e) + { + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; + return 0; + } + } + + return 1; +} From 0fe2dfeda692f6306a7984820496c121eac99971 Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Sat, 24 Jul 2021 17:45:06 -0700 Subject: [PATCH 16/28] [VS] Fix for VS test failures (#1836) * Purge swss-common before installing --- .azure-pipelines/docker-sonic-vs/Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.azure-pipelines/docker-sonic-vs/Dockerfile b/.azure-pipelines/docker-sonic-vs/Dockerfile index d425a6ffea..6feb8a7dfe 100644 --- a/.azure-pipelines/docker-sonic-vs/Dockerfile +++ b/.azure-pipelines/docker-sonic-vs/Dockerfile @@ -4,6 +4,15 @@ ARG docker_container_name ADD ["debs", "/debs"] +RUN dpkg --purge python-swsscommon +RUN dpkg --purge python3-swsscommon +RUN dpkg --purge swss +RUN dpkg --purge libsairedis +RUN dpkg --purge libswsscommon +RUN dpkg --purge libsaimetadata +RUN dpkg --purge libsaivs +RUN dpkg --purge syncd-vs + 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 From 7f80f06a98e2a18c4e5db4d5be25a62f6ac95bf6 Mon Sep 17 00:00:00 2001 From: Neetha John Date: Mon, 26 Jul 2021 10:42:27 -0700 Subject: [PATCH 17/28] Td2: Reclaim buffer from unused ports (#1830) Signed-off-by: Neetha John By default, lossless profiles are attached to PGs 3, 4 of all ports which results in a lot of buffer wastage when most of those ports are unused (not associated with a DEVICE_NEIGHBOR). In TD2, ingress pool size comprises of the reserved space as well. Hence making use of a special cable length of '0m' to identify unused ports and skip reserving space for those ports What I did * In buffermgr, if port with cable len '0m' is identified, return immediately without creating pg lossless profile or attaching profile to the lossless pgs of that port * Listen to 'admin_status' update as well from the PORT table and update port-speed mapping. This is to handle add-rack scenario where a port is added later - The port starts of with cable length 0m - Configlet to add a port is applied. The order of operations specifc to the PORT/CABLE_LENGTH table are - port is initially set to admin down, cable length is updated for that port, port table attributes are defined and port is set to admin up - speed update might not be seen when the port is set to admin up. Hence port-speed mapping will capture the speed update whenever its seen and once the cable length is updated while the port is brought back up, profiles can be attached to the lossless pgs of the port How I verified it - Manual tests done. Verified that no space is reserved for unused ports - Verified that when a port is added using 'add-rack' scenario, profile is attached to pgs of that port - New VS test added --- cfgmgr/buffermgr.cpp | 25 ++++-- cfgmgr/buffermgr.h | 4 +- tests/test_buffer_traditional.py | 145 +++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 tests/test_buffer_traditional.py diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index 7b1b73faff..684b17f31d 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -86,6 +86,7 @@ void BufferMgr::readPgProfileLookupFile(string file) task_process_status BufferMgr::doCableTask(string port, string cable_length) { m_cableLenLookup[port] = cable_length; + SWSS_LOG_INFO("Cable length set to %s for port %s", m_cableLenLookup[port].c_str(), port.c_str()); return task_process_status::task_success; } @@ -120,10 +121,11 @@ Create/update two tables: profile (in m_cfgBufferProfileTable) and port buffer ( } } */ -task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed) +task_process_status BufferMgr::doSpeedUpdateTask(string port) { vector fvVector; string cable; + string speed; if (m_cableLenLookup.count(port) == 0) { @@ -132,7 +134,13 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed) } cable = m_cableLenLookup[port]; + if (cable == "0m") + { + SWSS_LOG_NOTICE("Not creating/updating PG profile for port %s. Cable length is set to %s", port.c_str(), cable.c_str()); + return task_process_status::task_success; + } + speed = m_speedLookup[port]; if (m_pgProfileLookup.count(speed) == 0 || m_pgProfileLookup[speed].count(cable) == 0) { SWSS_LOG_ERROR("Unable to create/update PG profile for port %s. No PG profile configured for speed %s and cable length %s", @@ -368,11 +376,18 @@ void BufferMgr::doTask(Consumer &consumer) // receive and cache cable length table task_status = doCableTask(fvField(i), fvValue(i)); } - // In case of PORT table update, Buffer Manager is interested in speed update only - if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && fvField(i) == "speed") + if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && (fvField(i) == "speed" || fvField(i) == "admin_status")) { - // create/update profile for port - task_status = doSpeedUpdateTask(port, fvValue(i)); + if (fvField(i) == "speed") + { + m_speedLookup[port] = fvValue(i); + } + + if (m_speedLookup.count(port) != 0) + { + // create/update profile for port + task_status = doSpeedUpdateTask(port); + } } if (task_status != task_process_status::task_success) { diff --git a/cfgmgr/buffermgr.h b/cfgmgr/buffermgr.h index 5ad88f8901..c9777d2918 100644 --- a/cfgmgr/buffermgr.h +++ b/cfgmgr/buffermgr.h @@ -27,6 +27,7 @@ typedef std::map speed_map_t; typedef std::map pg_profile_lookup_t; typedef std::map port_cable_length_t; +typedef std::map port_speed_t; class BufferMgr : public Orch { @@ -52,10 +53,11 @@ class BufferMgr : public Orch pg_profile_lookup_t m_pgProfileLookup; port_cable_length_t m_cableLenLookup; + port_speed_t m_speedLookup; std::string getPgPoolMode(); void readPgProfileLookupFile(std::string); task_process_status doCableTask(std::string port, std::string cable_length); - task_process_status doSpeedUpdateTask(std::string port, std::string speed); + task_process_status doSpeedUpdateTask(std::string port); void doBufferTableTask(Consumer &consumer, ProducerStateTable &applTable); void transformSeperator(std::string &name); diff --git a/tests/test_buffer_traditional.py b/tests/test_buffer_traditional.py new file mode 100644 index 0000000000..b21862c296 --- /dev/null +++ b/tests/test_buffer_traditional.py @@ -0,0 +1,145 @@ +import pytest +import time + + +class TestBuffer(object): + LOSSLESS_PGS = [3, 4] + INTF = "Ethernet0" + + def setup_db(self, dvs): + self.app_db = dvs.get_app_db() + self.asic_db = dvs.get_asic_db() + self.config_db = dvs.get_config_db() + self.counter_db = dvs.get_counters_db() + + # enable PG watermark + self.set_pg_wm_status('enable') + + def get_pg_oid(self, pg): + fvs = dict() + fvs = self.counter_db.get_entry("COUNTERS_PG_NAME_MAP", "") + return fvs[pg] + + def set_pg_wm_status(self, state): + fvs = {'FLEX_COUNTER_STATUS': state} + self.config_db.update_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK", fvs) + time.sleep(1) + + def teardown(self): + # disable PG watermark + self.set_pg_wm_status('disable') + + def get_asic_buf_profile(self): + return set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) + + def check_new_profile_in_asic_db(self, profile): + retry_count = 0 + self.new_profile = None + while retry_count < 5: + retry_count += 1 + diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.orig_profiles + if len(diff) == 1: + self.new_profile = diff.pop() + break + else: + time.sleep(1) + assert self.new_profile, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count) + + def get_asic_buf_pg_profiles(self): + self.buf_pg_profile = dict() + for pg in self.pg_name_map: + buf_pg_entries = self.asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg]) + self.buf_pg_profile[pg] = buf_pg_entries["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] + + def change_cable_len(self, cable_len): + fvs = dict() + fvs[self.INTF] = cable_len + self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs) + + @pytest.fixture + def setup_teardown_test(self, dvs): + try: + self.setup_db(dvs) + pg_name_map = dict() + for pg in self.LOSSLESS_PGS: + pg_name = "{}:{}".format(self.INTF, pg) + pg_name_map[pg_name] = self.get_pg_oid(pg_name) + yield pg_name_map + finally: + self.teardown() + + def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): + self.pg_name_map = setup_teardown_test + orig_cable_len = None + orig_speed = None + try: + dvs.runcmd("config interface startup {}".format(self.INTF)) + self.orig_profiles = self.get_asic_buf_profile() + # get orig cable length and speed + fvs = self.config_db.get_entry("CABLE_LENGTH", "AZURE") + orig_cable_len = fvs[self.INTF] + fvs = self.config_db.get_entry("PORT", self.INTF) + orig_speed = fvs["speed"] + + if orig_speed == "100000": + test_speed = "40000" + elif orig_speed == "40000": + test_speed = "100000" + test_cable_len = "0m" + + # check if the lossless profile for the test speed is already present + fvs = dict() + new_lossless_profile = "pg_lossless_{}_{}_profile".format(test_speed, orig_cable_len) + fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", new_lossless_profile) + if len(fvs): + profile_exp_cnt_diff = 0 + else: + profile_exp_cnt_diff = 1 + + # get the orig buf profiles attached to the pgs + self.get_asic_buf_pg_profiles() + + # change cable length to 'test_cable_len' + self.change_cable_len(test_cable_len) + + # change intf speed to 'test_speed' + dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + test_lossless_profile = "pg_lossless_{}_{}_profile".format(test_speed, test_cable_len) + # buffer profile should not get created + self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", test_lossless_profile) + + # buffer pgs should still point to the original buffer profile + orig_lossless_profile = "pg_lossless_{}_{}_profile".format(orig_speed, orig_cable_len) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", self.INTF + ":3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(orig_lossless_profile)}) + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.buf_pg_profile[pg] + self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + + # change cable length to 'orig_cable_len' + self.change_cable_len(orig_cable_len) + + # change intf speed to 'test_speed' + dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + if profile_exp_cnt_diff != 0: + # new profile will get created + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", new_lossless_profile) + self.check_new_profile_in_asic_db(new_lossless_profile) + # verify that buffer pgs point to the new profile + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.new_profile + self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + + else: + # verify that buffer pgs do not point to the old profile since we cannot deduce the new profile oid + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.buf_pg_profile[pg] + self.asic_db.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + finally: + if orig_cable_len: + self.change_cable_len(orig_cable_len) + if orig_speed: + dvs.runcmd("config interface speed {} {}".format(self.INTF, orig_speed)) + dvs.runcmd("config interface shutdown {}".format(self.INTF)) From ec104c1ba079901a4af1fb72981d34e0069ceeac Mon Sep 17 00:00:00 2001 From: byu343 Date: Mon, 26 Jul 2021 14:19:32 -0700 Subject: [PATCH 18/28] [gearbox] Set context for phys based on configs (#1826) SAI_REDIS_SWITCH_ATTR_CONTEXT was set according to the phy_id in gearbox_config.json. This change will set it based on the context_id provided in that json file. The reason behind it is to support the case of multiple phys sharing the same context. --- gearsyncd/gearboxparser.cpp | 8 ++++++++ lib/gearboxutils.cpp | 4 ++++ lib/gearboxutils.h | 1 + orchagent/saihelper.cpp | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/gearsyncd/gearboxparser.cpp b/gearsyncd/gearboxparser.cpp index 863ee57dab..1ae8118266 100644 --- a/gearsyncd/gearboxparser.cpp +++ b/gearsyncd/gearboxparser.cpp @@ -143,6 +143,14 @@ bool GearboxParser::parse() val = phy["bus_id"]; attr = std::make_pair("bus_id", std::to_string(val.get())); attrs.push_back(attr); + if (phy.find("context_id") == phy.end()) + { + SWSS_LOG_ERROR("missing 'context_id' field in 'phys' item %d in gearbox configuration", iter); + return false; + } + val = phy["context_id"]; + attr = std::make_pair("context_id", std::to_string(val.get())); + attrs.push_back(attr); if (phy.find("hwinfo") == phy.end()) { SWSS_LOG_ERROR("missing 'hwinfo' field in 'phys' item %d in gearbox configuration", iter); diff --git a/lib/gearboxutils.cpp b/lib/gearboxutils.cpp index 989de4d22f..30c89ab494 100644 --- a/lib/gearboxutils.cpp +++ b/lib/gearboxutils.cpp @@ -185,6 +185,10 @@ std::map GearboxUtils::loadPhyMap(Table *gearboxTable) { phy.bus_id = std::stoi(val.second); } + else if (val.first == "context_id") + { + phy.context_id = std::stoi(val.second); + } } gearboxPhyMap[phy.phy_id] = phy; } diff --git a/lib/gearboxutils.h b/lib/gearboxutils.h index 0a70c62d77..b28b7b1080 100644 --- a/lib/gearboxutils.h +++ b/lib/gearboxutils.h @@ -44,6 +44,7 @@ typedef struct std::string hwinfo; uint32_t address; uint32_t bus_id; + uint32_t context_id; } gearbox_phy_t; typedef struct diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index dac7b1e92f..15febdb518 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -418,7 +418,7 @@ sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy) /* Must be last Attribute */ attr.id = SAI_REDIS_SWITCH_ATTR_CONTEXT; - attr.value.u64 = phy->phy_id; + attr.value.u64 = phy->context_id; attrs.push_back(attr); status = sai_switch_api->create_switch(&phyOid, (uint32_t)attrs.size(), attrs.data()); From 237b89cd4e0bb1bada0f9e9b845f412fb7c6d6d3 Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Thu, 29 Jul 2021 16:31:01 +0800 Subject: [PATCH 19/28] [Dynamic Buffer Calc] Bug fix: Don't create lossless buffer profile for active ports without speed configured (#1822) **What I did** Bugfix: Don't create lossless buffer profiles for active ports without speed configured Root cause: - In `handlePortTableUpdate`, `refreshPgsFromPort` is called if admin status is updated even if the speed is not configured. This is reasonable because the port can be configured as headroom override and the profile should be applied in that case. - However, as a side-effect, the port's state is set to `PORT_READY` in `refreshPgsForPort` regardless of whether the speed is configured, which is not correct. This should be avoided and `PORT_READY` should be set by the caller if necessary Fix: - Don't set the port's state to `PORT_READY` in `refreshPgsForPort` and check the port's state before calling it. - Explicitly set port's state to `PORT_READY` in `handlePortStateTable`. This has been done by all other callers. Note: - The change is covered by the existing vs tests. - The scenario where the bug was originally found can not be covered by vs test because: - The speed is always configured in vs image by default - Removing speed is not handled in buffer manager since it's not a valid flow. - A regression test will be opened to cover this case. --- cfgmgr/buffermgrdyn.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 7b4b19388d..071fec6f78 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -987,8 +987,6 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons SWSS_LOG_DEBUG("Nothing to do for port %s since no PG configured on it", port.c_str()); } - portInfo.state = PORT_READY; - // Remove the old profile which is probably not referenced anymore. if (!profilesToBeReleased.empty()) { @@ -1217,6 +1215,9 @@ 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); + if (port.state != PORT_READY) + continue; + rc = refreshPgsForPort(portName, port.effective_speed, port.cable_length, port.mtu); if (task_process_status::task_success != rc) { @@ -1452,6 +1453,7 @@ task_process_status BufferMgrDynamic::handlePortStateTable(KeyOpFieldsValuesTupl { if (isNonZero(portInfo.cable_length) && portInfo.state != PORT_ADMIN_DOWN) { + portInfo.state = PORT_READY; refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu); } } From e65aec9f9c5e8c055bcf56a81bc8d5446eda8fad Mon Sep 17 00:00:00 2001 From: Prince Sunny Date: Mon, 2 Aug 2021 08:05:04 -0700 Subject: [PATCH 20/28] Bridge mac setting, fix statedb time format (#1844) *Set bridge mac same as Vlan mac *Format state_db entries to have six digit precision for microseconds --- cfgmgr/vlanmgr.cpp | 3 ++- orchagent/muxorch.cpp | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index e439420933..830f8d9d26 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -196,7 +196,8 @@ bool VlanMgr::setHostVlanMac(int vlan_id, const string &mac) // The command should be generated as: // /sbin/ip link set Vlan{{vlan_id}} address {{mac}} ostringstream cmds; - cmds << IP_CMD " link set " VLAN_PREFIX + std::to_string(vlan_id) + " address " << shellquote(mac); + cmds << IP_CMD " link set " VLAN_PREFIX + std::to_string(vlan_id) + " address " << shellquote(mac) << " && " + IP_CMD " link set " DOT1Q_BRIDGE_NAME " address " << shellquote(mac); std::string res; EXEC_WITH_ERROR_THROW(cmds.str(), res); diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 503ff3cd16..6cfc6bc4d5 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -1300,7 +1300,17 @@ void MuxCableOrch::updateMuxMetricState(string portName, string muxState, bool s char buf[256]; std::strftime(buf, 256, "%Y-%b-%d %H:%M:%S.", &now_tm); - string time = string(buf) + to_string(micros); + /* + * Prepend '0's for 6 point precision + */ + const int precision = 6; + auto ms = to_string(micros); + if (ms.length() < precision) + { + ms.insert(ms.begin(), precision - ms.length(), '0'); + } + + string time = string(buf) + ms; mux_metric_table_.hset(portName, msg, time); } From fe0dba0e9fc52b5199082a4c099e5c3a40519dc8 Mon Sep 17 00:00:00 2001 From: Nazarii Hnydyn Date: Mon, 2 Aug 2021 19:06:29 +0300 Subject: [PATCH 21/28] [cfgmgr]: Introduce common libs. (#1842) What I did Refactored cfgmgr Makefile Why I did it To avoid code duplication of common libs How I verified it Build swss Signed-off-by: Nazarii Hnydyn --- cfgmgr/Makefile.am | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index d1ea5978c1..68bec2d3a0 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -3,6 +3,7 @@ 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 +COMMON_LIBS = -lswsscommon bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd @@ -25,64 +26,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 $(SAIMETA_LIBS) +vlanmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +teammgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +portmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +intfmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +buffermgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +vrfmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) $(LIBNL_LIBS) +nbrmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +vxlanmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +sflowmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +natmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +coppmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +tunnelmgrd_LDADD = $(COMMON_LIBS) $(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 $(SAIMETA_LIBS) +macsecmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) From 72a72f8b84d3752fac3316783971d45d4c9d5978 Mon Sep 17 00:00:00 2001 From: Ann Pokora <44511240+qbdwlr@users.noreply.github.com> Date: Mon, 2 Aug 2021 21:30:07 -0400 Subject: [PATCH 22/28] [crm] Fix for Issue Azure/sonic-buildimage#8036 (#1829) *Added more appropriate log message for CRM queries of MPLS_INSEG and MPLS_NEXTHOP resources when platform does not support these resource types. --- orchagent/crmorch.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index bdd899057a..1e67a72087 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -565,6 +565,16 @@ void CrmOrch::getResAvailableCounters() sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 0, nullptr, &availCount); if (status != SAI_STATUS_SUCCESS) { + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + // mark unsupported resources + res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; + SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); + break; + } SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); break; } @@ -585,6 +595,16 @@ void CrmOrch::getResAvailableCounters() sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 1, &attr, &availCount); if (status != SAI_STATUS_SUCCESS) { + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + // mark unsupported resources + res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; + SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); + break; + } SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); break; } From 7aca82df3b56276625ce3d70ffb45b849003b0d8 Mon Sep 17 00:00:00 2001 From: Praveen-Brcm <51765988+Praveen-Brcm@users.noreply.github.com> Date: Tue, 3 Aug 2021 17:29:56 -0700 Subject: [PATCH 23/28] Mclag enhacements support code changes. (#1331) * Mclag enhacements support code changes. * Adding change to allow MCLAG remote MAC move. * Added support for adding mclag remote mac to kernel, on top of PR-1276 * Updating the change from PR1276 and PR885. * Adding new orchfiles to mock_tests * MCLAG Unique IP support changes. * Removed dependency with PR 885. * Adding observer support for mlagorch. * Fixed FDB notifiation issue * Fixing the test_mclag_fdb type attributes. * Remove as the change may not be supported on non-brcm for PortChannel settings. * Removing the isolation group handling from Mlagorch, Isolation group now will be added/updated only via mclagsyncd updates. * Added back the update function. Co-authored-by: Tapash Das --- fdbsyncd/fdbsync.cpp | 162 ++++++- fdbsyncd/fdbsync.h | 18 +- fdbsyncd/fdbsyncd.cpp | 5 + orchagent/Makefile.am | 2 + orchagent/fdborch.cpp | 303 +++++++++++-- orchagent/fdborch.h | 7 +- orchagent/isolationgrouporch.cpp | 749 +++++++++++++++++++++++++++++++ orchagent/isolationgrouporch.h | 142 ++++++ orchagent/mlagorch.cpp | 250 +++++++++++ orchagent/mlagorch.h | 66 +++ orchagent/observer.h | 8 +- orchagent/orchdaemon.cpp | 23 +- orchagent/orchdaemon.h | 2 + orchagent/portsorch.cpp | 7 + orchagent/saihelper.cpp | 2 + tests/mock_tests/Makefile.am | 2 + tests/mock_tests/aclorch_ut.cpp | 12 +- tests/test_mclag_cfg.py | 238 ++++++++++ tests/test_mclag_fdb.py | 505 +++++++++++++++++++++ 19 files changed, 2450 insertions(+), 53 deletions(-) create mode 100644 orchagent/isolationgrouporch.cpp create mode 100644 orchagent/isolationgrouporch.h create mode 100644 orchagent/mlagorch.cpp create mode 100644 orchagent/mlagorch.h create mode 100644 tests/test_mclag_cfg.py create mode 100644 tests/test_mclag_fdb.py diff --git a/fdbsyncd/fdbsync.cpp b/fdbsyncd/fdbsync.cpp index 5a5caf5a89..0cdcc63214 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -25,6 +25,7 @@ FdbSync::FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector m_fdbTable(pipelineAppDB, APP_VXLAN_FDB_TABLE_NAME), m_imetTable(pipelineAppDB, APP_VXLAN_REMOTE_VNI_TABLE_NAME), m_fdbStateTable(stateDb, STATE_FDB_TABLE_NAME), + m_mclagRemoteFdbStateTable(stateDb, STATE_MCLAG_REMOTE_FDB_TABLE_NAME), m_cfgEvpnNvoTable(config_db, CFG_VXLAN_EVPN_NVO_TABLE_NAME) { m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "fdbsyncd", "swss", DEFAULT_FDBSYNC_WARMSTART_TIMER); @@ -43,7 +44,6 @@ FdbSync::~FdbSync() } } - // Check if interface entries are restored in kernel bool FdbSync::isIntfRestoreDone() { @@ -180,6 +180,69 @@ void FdbSync::processStateFdb() } } +void FdbSync::processStateMclagRemoteFdb() +{ + struct m_fdb_info info; + std::deque entries; + + m_mclagRemoteFdbStateTable.pops(entries); + + int count =0 ; + for (auto entry: entries) + { + count++; + std::string key = kfvKey(entry); + std::string op = kfvOp(entry); + + std::size_t delimiter = key.find_first_of(":"); + auto vlan_name = key.substr(0, delimiter); + auto mac_address = key.substr(delimiter+1); + + info.vid = vlan_name; + info.mac = mac_address; + + if(op == "SET") + { + info.op_type = FDB_OPER_ADD ; + } + else + { + info.op_type = FDB_OPER_DEL ; + } + + SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); + + for (auto i : kfvFieldsValues(entry)) + { + SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " + "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); + + if(fvField(i) == "port") + { + info.port_name = fvValue(i); + } + + if(fvField(i) == "type") + { + if(fvValue(i) == "dynamic") + { + info.type = FDB_TYPE_DYNAMIC; + } + else if (fvValue(i) == "static") + { + info.type = FDB_TYPE_STATIC; + } + } + } + + if (op != "SET" && macCheckSrcDB(&info) == false) + { + continue; + } + updateMclagRemoteMac(&info); + } +} + void FdbSync::macUpdateCache(struct m_fdb_info *info) { string key = info->vid + ":" + info->mac; @@ -189,6 +252,15 @@ void FdbSync::macUpdateCache(struct m_fdb_info *info) return; } +void FdbSync::macUpdateMclagRemoteCache(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + m_mclag_remote_fdb_mac[key].port_name = info->port_name; + m_mclag_remote_fdb_mac[key].type = info->type; + + return; +} + bool FdbSync::macCheckSrcDB(struct m_fdb_info *info) { string key = info->vid + ":" + info->mac; @@ -331,6 +403,84 @@ void FdbSync::addLocalMac(string key, string op) return; } +void FdbSync::updateMclagRemoteMac (struct m_fdb_info *info) +{ + char *op; + char *type; + string port_name = ""; + string key = info->vid + ":" + info->mac; + short fdb_type; /*dynamic or static*/ + + if (info->op_type == FDB_OPER_ADD) + { + macUpdateMclagRemoteCache(info); + op = "replace"; + port_name = info->port_name; + fdb_type = info->type; + } + else + { + op = "del"; + port_name = m_mclag_remote_fdb_mac[key].port_name; + fdb_type = m_mclag_remote_fdb_mac[key].type; + m_mclag_remote_fdb_mac.erase(key); + } + + if (fdb_type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + info->mac + " dev " + + port_name + " master " + type + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + + SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::updateMclagRemoteMacPort(int ifindex, int vlan, std::string mac) +{ + string key = "Vlan" + to_string(vlan) + ":" + mac; + int type = 0; + string port_name = ""; + + SWSS_LOG_INFO("Updating Intf %d, Vlan:%d MAC:%s Key %s", ifindex, vlan, mac.c_str(), key.c_str()); + + if (m_mclag_remote_fdb_mac.find(key) != m_mclag_remote_fdb_mac.end()) + { + type = m_mclag_remote_fdb_mac[key].type; + port_name = m_mclag_remote_fdb_mac[key].port_name; + SWSS_LOG_INFO(" port %s, type %d\n", port_name.c_str(), type); + + if (type == FDB_TYPE_STATIC) + { + const std::string cmds = std::string("") + + " bridge fdb replace" + " " + mac + " dev " + + port_name + " master static vlan " + to_string(vlan); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_NOTICE("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + return; + } + + SWSS_LOG_NOTICE("Update cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + } + return; +} + /* * This is a special case handling where mac is learned in the ASIC. * Then MAC is learned in the Kernel, Since this mac is learned in the Kernel @@ -573,6 +723,16 @@ void FdbSync::onMsgNbr(int nlmsg_type, struct nl_object *obj) if (isVxlanIntf == false) { + if (nlmsg_type == RTM_NEWNEIGH) + { + int vid = rtnl_neigh_get_vlan(neigh); + int state = rtnl_neigh_get_state(neigh); + if (state & NUD_PERMANENT) + { + updateMclagRemoteMacPort(ifindex, vid, macStr); + } + } + if (nlmsg_type != RTM_DELNEIGH) { return; diff --git a/fdbsyncd/fdbsync.h b/fdbsyncd/fdbsync.h index 927b95dd0b..ab55b91a31 100644 --- a/fdbsyncd/fdbsync.h +++ b/fdbsyncd/fdbsync.h @@ -64,6 +64,11 @@ class FdbSync : public NetMsg return &m_fdbStateTable; } + SubscriberStateTable *getMclagRemoteFdbStateTable() + { + return &m_mclagRemoteFdbStateTable; + } + SubscriberStateTable *getCfgEvpnNvoTable() { return &m_cfgEvpnNvoTable; @@ -71,6 +76,8 @@ class FdbSync : public NetMsg void processStateFdb(); + void processStateMclagRemoteFdb(); + void processCfgEvpnNvo(); bool m_reconcileDone = false; @@ -81,6 +88,7 @@ class FdbSync : public NetMsg ProducerStateTable m_fdbTable; ProducerStateTable m_imetTable; SubscriberStateTable m_fdbStateTable; + SubscriberStateTable m_mclagRemoteFdbStateTable; AppRestartAssist *m_AppRestartAssist; SubscriberStateTable m_cfgEvpnNvoTable; @@ -89,7 +97,9 @@ class FdbSync : public NetMsg std::string port_name; short type;/*dynamic or static*/ }; - std::unordered_map m_fdb_mac; + std::unordered_map m_fdb_mac; + + std::unordered_map m_mclag_remote_fdb_mac; void macDelVxlanEntry(std::string auxkey, struct m_fdb_info *info); @@ -103,6 +113,12 @@ class FdbSync : public NetMsg void macRefreshStateDB(int vlan, std::string kmac); + void updateMclagRemoteMac(struct m_fdb_info *info); + + void updateMclagRemoteMacPort(int ifindex, int vlan, std::string mac); + + void macUpdateMclagRemoteCache(struct m_fdb_info *info); + bool checkImetExist(std::string key, uint32_t vni); bool checkDelImet(std::string key, uint32_t vni); diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index 6b9724d89e..a83b2693e1 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -86,6 +86,7 @@ int main(int argc, char **argv) netlink.dumpRequest(RTM_GETNEIGH); s.addSelectable(sync.getFdbStateTable()); + s.addSelectable(sync.getMclagRemoteFdbStateTable()); s.addSelectable(sync.getCfgEvpnNvoTable()); while (true) { @@ -95,6 +96,10 @@ int main(int argc, char **argv) { sync.processStateFdb(); } + else if (temps == (Selectable *)sync.getMclagRemoteFdbStateTable()) + { + sync.processStateMclagRemoteFdb(); + } else if (temps == (Selectable *)sync.getCfgEvpnNvoTable()) { sync.processCfgEvpnNvo(); diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 05abdfbcec..f4317134f6 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -64,6 +64,8 @@ orchagent_SOURCES = \ chassisorch.cpp \ debugcounterorch.cpp \ natorch.cpp \ + mlagorch.cpp \ + isolationgrouporch.cpp \ muxorch.cpp \ macsecorch.cpp \ lagid.cpp diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 229dec0b15..6094c12c05 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -11,6 +11,7 @@ #include "crmorch.h" #include "notifier.h" #include "sai_serialize.h" +#include "mlagorch.h" #include "vxlanorch.h" #include "directory.h" @@ -19,14 +20,17 @@ extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; +extern MlagOrch* gMlagOrch; extern Directory gDirectory; const int FdbOrch::fdborch_pri = 20; -FdbOrch::FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port) : +FdbOrch::FdbOrch(DBConnector* applDbConnector, vector appFdbTables, + TableConnector stateDbFdbConnector, TableConnector stateDbMclagFdbConnector, PortsOrch *port) : Orch(applDbConnector, appFdbTables), m_portsOrch(port), - m_fdbStateTable(stateDbFdbConnector.first, stateDbFdbConnector.second) + m_fdbStateTable(stateDbFdbConnector.first, stateDbFdbConnector.second), + m_mclagFdbStateTable(stateDbMclagFdbConnector.first, stateDbMclagFdbConnector.second) { for(auto it: appFdbTables) { @@ -61,6 +65,7 @@ bool FdbOrch::bake() return true; } + bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) { const FdbEntry& entry = update.entry; @@ -92,8 +97,11 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) */ if (port.m_bridge_port_id == it->second.bridge_port_id) { - SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); - return false; + if (it->second.origin != FDB_ORIGIN_MCLAG_ADVERTIZED) + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); + return false; + } } mac_move = true; oldFdbData = it->second; @@ -112,6 +120,13 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) SWSS_LOG_INFO("m_entries size=%zu mac=%s port=0x%" PRIx64, m_entries.size(), entry.mac.to_string().c_str(), m_entries[entry].bridge_port_id); + if (mac_move && (oldFdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + SWSS_LOG_NOTICE("fdbEvent: FdbOrch MCLAG remote to local move delete mac from state MCLAG remote fdb %s table:" + "bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); + + m_mclagFdbStateTable.del(key); + } // Write to StateDb std::vector fvs; fvs.push_back(FieldValueTuple("port", portName)); @@ -140,7 +155,15 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) return false; } - if (oldFdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if (oldFdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + SWSS_LOG_NOTICE("fdbEvent: FdbOrch MCLAG remote mac %s deleted, remove from state mclag remote fdb table:" + "bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); + m_mclagFdbStateTable.del(key); + } + + if ((oldFdbData.origin == FDB_ORIGIN_LEARN) || + (oldFdbData.origin == FDB_ORIGIN_PROVISIONED)) { // Remove in StateDb for non advertised mac addresses m_fdbStateTable.del(key); @@ -205,10 +228,76 @@ void FdbOrch::update(sai_fdb_event_t type, auto existing_entry = m_entries.find(update.entry); if (existing_entry != m_entries.end()) { - SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" + if (existing_entry->second.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + // If the bp is different MOVE the MAC entry. + if (existing_entry->second.bridge_port_id != bridge_port_id) + { + Port port; + SWSS_LOG_NOTICE("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "with different existing-bp 0x%" PRIx64 " new-bp:0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); + if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, port)) + { + SWSS_LOG_NOTICE("FdbOrch LEARN notification: Failed to get port by bridge port ID 0x%" PRIx64, existing_entry->second.bridge_port_id); + return; + } + else + { + port.m_fdb_count--; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + } + // Continue to add (update/move) the MAC + } + else + { + SWSS_LOG_NOTICE("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "with same bp 0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id); + // Continue to move the MAC as local. + + // Existing MAC entry is on same VLAN, Port with Origin MCLAG(remote), its possible after the local learn MAC in + //the HW is updated to remote from FdbOrch, Update the MAC back to local in HW so that FdbOrch and HW is Sync and aging enabled. + sai_status_t status; + sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry->mac_address, sizeof(sai_mac_t)); + fdb_entry.bv_id = entry->bv_id; + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_DYNAMIC; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = existing_entry->second.bridge_port_id; + attrs.push_back(attr); + + for(auto itr : attrs) + { + status = sai_fdb_api->set_fdb_entry_attribute(&fdb_entry, &itr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("macUpdate-Failed for MCLAG mac attr.id=0x%x for FDB %s in 0x%" PRIx64 "on %s, rv:%d", + itr.id, update.entry.mac.to_string().c_str(), entry->bv_id, update.port.m_alias.c_str(), status); + } + } + update.add = true; + update.type = "dynamic"; + storeFdbEntryState(update); + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + return; + } + } + else + { + SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "existing-bp 0x%" PRIx64 "new-bp:0x%" PRIx64, update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); - break; + } + break; } update.add = true; @@ -269,7 +358,7 @@ void FdbOrch::update(sai_fdb_event_t type, fdbData.remote_ip = existing_entry->second.remote_ip; fdbData.esi = existing_entry->second.esi; fdbData.vni = existing_entry->second.vni; - saved_fdb_entries[update.port.m_alias].push_back( + saved_fdb_entries[update.port.m_alias].push_back( {existing_entry->first.mac, vlan.m_vlan_info.vlan_id, fdbData}); } else @@ -300,6 +389,47 @@ void FdbOrch::update(sai_fdb_event_t type, } } + // If MAC is MCLAG remote do not delete for age event, Add the MAC back.. + if (existing_entry->second.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + sai_status_t status; + sai_fdb_entry_t fdb_entry; + + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry->mac_address, sizeof(sai_mac_t)); + fdb_entry.bv_id = entry->bv_id; + + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE; + attr.value.booldata = true; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = existing_entry->second.bridge_port_id; + attrs.push_back(attr); + + SWSS_LOG_NOTICE("fdbEvent: MAC age event received, MAC is MCLAG origin, added back" + "to HW type %s FDB %s in %s on %s", + existing_entry->second.type.c_str(), + update.entry.mac.to_string().c_str(), vlan.m_alias.c_str(), + update.port.m_alias.c_str()); + + status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s FDB %s in %s on %s, rv:%d", + existing_entry->second.type.c_str(), update.entry.mac.to_string().c_str(), + vlan.m_alias.c_str(), update.port.m_alias.c_str(), status); + } + return; + } + update.add = false; if (!update.port.m_alias.empty()) { @@ -401,10 +531,8 @@ void FdbOrch::update(sai_fdb_event_t type, storeFdbEntryState(update); - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); - } + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + } } else if (entry->bv_id == SAI_NULL_OBJECT_ID) @@ -424,11 +552,7 @@ void FdbOrch::update(sai_fdb_event_t type, update.add = false; storeFdbEntryState(update); - - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); - } + notify(SUBJECT_TYPE_FDB_CHANGE, &update); } itr = next_item; } @@ -536,6 +660,10 @@ void FdbOrch::doTask(Consumer& consumer) origin = FDB_ORIGIN_VXLAN_ADVERTIZED; } + if (table_name == APP_MCLAG_FDB_TABLE_NAME) + { + origin = FDB_ORIGIN_MCLAG_ADVERTIZED; + } auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) @@ -632,8 +760,7 @@ void FdbOrch::doTask(Consumer& consumer) } } - /* FDB type is either dynamic or static */ - assert(type == "dynamic" || type == "static"); + assert(type == "dynamic" || type == "dynamic_local" || type == "static" ); if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { @@ -656,14 +783,48 @@ void FdbOrch::doTask(Consumer& consumer) fdbData.esi = esi; fdbData.vni = vni; if (addFdbEntry(entry, port, fdbData)) + { + if (origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + if (type == "dynamic_local") + { + m_mclagFdbStateTable.del(key); + } + } + + if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + if(!remote_ip.length()) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(remote_ip); + } + + it = consumer.m_toSync.erase(it); + } else it++; } else if (op == DEL_COMMAND) { if (removeFdbEntry(entry, origin)) + { + if (origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + m_mclagFdbStateTable.del(key); + SWSS_LOG_NOTICE("fdbEvent: do Task Delete MCLAG FDB from state mclag remote fdb table: " + "Mac: %s Vlan: %d ",entry.mac.to_string().c_str(), vlan.m_vlan_info.vlan_id ); + } + it = consumer.m_toSync.erase(it); + } else it++; @@ -892,10 +1053,7 @@ void FdbOrch::notifyObserversFDBFlush(Port &port, sai_object_id_t& bvid) if (!flushUpdate.entries.empty()) { - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_FLUSH_CHANGE, &flushUpdate); - } + notify(SUBJECT_TYPE_FDB_FLUSH_CHANGE, &flushUpdate); } } @@ -1007,6 +1165,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, string oldType; FdbOrigin oldOrigin = FDB_ORIGIN_INVALID ; bool macUpdate = false; + auto it = m_entries.find(entry); if (it != m_entries.end()) { @@ -1065,6 +1224,20 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, it->second.remote_ip.c_str()); } } + else if ((oldOrigin == FDB_ORIGIN_LEARN) && (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + if ((port.m_bridge_port_id == it->second.bridge_port_id) && (oldType == "dynamic") && (fdbData.type == "dynamic_local")) + { + SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d old_origin=%d" + " old_type=%s local mac exists," + " received dynamic_local from iccpd, ignore update", + entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), + fdbData.type.c_str(), fdbData.origin, oldOrigin, oldType.c_str()); + + return true; + } + } + } else /* (fdbData.origin == oldOrigin) */ { @@ -1085,15 +1258,21 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, attr.id = SAI_FDB_ENTRY_ATTR_TYPE; if (fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { - attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + } + else if (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + attr.value.s32 = (fdbData.type == "dynamic_local") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; } else { attr.value.s32 = (fdbData.type == "dynamic") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; } + attrs.push_back(attr); - if ((fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) && (fdbData.type == "dynamic")) + if ((fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) || (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + || (fdbData.type == "dynamic")) { attr.id = SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE; attr.value.booldata = true; @@ -1148,7 +1327,6 @@ 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", @@ -1201,12 +1379,21 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, FdbData storeFdbData = fdbData; storeFdbData.bridge_port_id = port.m_bridge_port_id; + // overwrite the type and origin + if ((fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.type == "dynamic_local")) + { + //If the MAC is dynamic_local change the origin accordingly + //MAC is added/updated as dynamic to allow aging. + storeFdbData.origin = FDB_ORIGIN_LEARN; + storeFdbData.type = "dynamic"; + } m_entries[entry] = storeFdbData; string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); - if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if ((fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED) && + (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED)) { /* State-DB is updated only for Local Mac addresses */ // Write to StateDb @@ -1218,7 +1405,9 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, fvs.push_back(FieldValueTuple("type", fdbData.type)); m_fdbStateTable.set(key, fvs); } - else if (macUpdate && (oldOrigin != FDB_ORIGIN_VXLAN_ADVERTIZED)) + + else if (macUpdate && (oldOrigin != FDB_ORIGIN_MCLAG_ADVERTIZED) && + (oldOrigin != FDB_ORIGIN_VXLAN_ADVERTIZED)) { /* origin is FDB_ORIGIN_ADVERTIZED and it is mac-update * so delete from StateDb since we only keep local fdbs @@ -1227,6 +1416,26 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, m_fdbStateTable.del(key); } + if ((fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.type != "dynamic_local")) + { + std::vector fvs; + fvs.push_back(FieldValueTuple("port", port_name)); + fvs.push_back(FieldValueTuple("type", fdbData.type)); + m_mclagFdbStateTable.set(key, fvs); + + SWSS_LOG_NOTICE("fdbEvent: AddFdbEntry: Add MCLAG MAC with state mclag remote fdb table " + "Mac: %s Vlan: %d port:%s type:%s", entry.mac.to_string().c_str(), + vlan.m_vlan_info.vlan_id, port_name.c_str(), fdbData.type.c_str()); + } + else if (macUpdate && (oldOrigin == FDB_ORIGIN_MCLAG_ADVERTIZED) && + (fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + SWSS_LOG_NOTICE("fdbEvent: AddFdbEntry: del MCLAG MAC from state MCLAG remote fdb table " + "Mac: %s Vlan: %d port:%s type:%s", entry.mac.to_string().c_str(), + vlan.m_vlan_info.vlan_id, port_name.c_str(), fdbData.type.c_str()); + m_mclagFdbStateTable.del(key); + } + if (!macUpdate) { gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); @@ -1277,20 +1486,32 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) if (fdbData.origin != origin) { - /* When mac is moved from remote to local - * BGP will delete the mac from vxlan_fdb_table - * but we should not delete this mac here since now - * mac in orchagent represents locally learnt - */ - SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb origin is different; found_origin:%d delete_origin:%d", - entry.mac.to_string().c_str(), fdbData.origin, origin); + if ((origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.origin == FDB_ORIGIN_LEARN) && + (port.m_oper_status == SAI_PORT_OPER_STATUS_DOWN) && (gMlagOrch->isMlagInterface(port.m_alias))) + { + //check if the local MCLAG port is down, if yes then continue delete the local MAC + origin = FDB_ORIGIN_LEARN; + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb del origin is MCLAG; delete local mac as port %s is down", + entry.mac.to_string().c_str(), port.m_alias.c_str()); + } + else + { - /* We may still have the mac in saved-fdb probably due to unavailability - * of bridge-port. check whether the entry is in the saved fdb, - * if so delete it from there. */ - deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); + /* When mac is moved from remote to local + * BGP will delete the mac from vxlan_fdb_table + * but we should not delete this mac here since now + * mac in orchagent represents locally learnt + */ + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb origin is different; found_origin:%d delete_origin:%d", + entry.mac.to_string().c_str(), fdbData.origin, origin); - return true; + /* We may still have the mac in saved-fdb probably due to unavailability + * of bridge-port. check whether the entry is in the saved fdb, + * if so delete it from there. */ + deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); + + return true; + } } string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); @@ -1323,7 +1544,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) && (fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED)) { m_fdbStateTable.del(key); } diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 201493f574..82611e686f 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -10,7 +10,8 @@ enum FdbOrigin FDB_ORIGIN_INVALID = 0, FDB_ORIGIN_LEARN = 1, FDB_ORIGIN_PROVISIONED = 2, - FDB_ORIGIN_VXLAN_ADVERTIZED = 4 + FDB_ORIGIN_VXLAN_ADVERTIZED = 4, + FDB_ORIGIN_MCLAG_ADVERTIZED = 8 }; struct FdbEntry @@ -80,7 +81,8 @@ class FdbOrch: public Orch, public Subject, public Observer { public: - FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port); + FdbOrch(DBConnector* applDbConnector, vector appFdbTables, + TableConnector stateDbFdbConnector, TableConnector stateDbMclagFdbConnector, PortsOrch *port); ~FdbOrch() { @@ -105,6 +107,7 @@ class FdbOrch: public Orch, public Subject, public Observer fdb_entries_by_port_t saved_fdb_entries; vector m_appTables; Table m_fdbStateTable; + Table m_mclagFdbStateTable; NotificationConsumer* m_flushNotificationsConsumer; NotificationConsumer* m_fdbNotificationConsumer; diff --git a/orchagent/isolationgrouporch.cpp b/orchagent/isolationgrouporch.cpp new file mode 100644 index 0000000000..0138a6ab95 --- /dev/null +++ b/orchagent/isolationgrouporch.cpp @@ -0,0 +1,749 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "isolationgrouporch.h" +#include "converter.h" +#include "tokenize.h" +#include "portsorch.h" + +extern sai_object_id_t gSwitchId; +extern PortsOrch *gPortsOrch; +extern sai_isolation_group_api_t* sai_isolation_group_api; +extern sai_bridge_api_t *sai_bridge_api; +extern sai_port_api_t *sai_port_api; +extern IsoGrpOrch *gIsoGrpOrch; + +IsoGrpOrch::IsoGrpOrch(vector &connectors) : Orch(connectors) +{ + SWSS_LOG_ENTER(); + gPortsOrch->attach(this); +} + +IsoGrpOrch::~IsoGrpOrch() +{ + SWSS_LOG_ENTER(); +} + +shared_ptr +IsoGrpOrch::getIsolationGroup(string name) +{ + SWSS_LOG_ENTER(); + + shared_ptr ret = nullptr; + + auto grp = m_isolationGrps.find(name); + if (grp != m_isolationGrps.end()) + { + ret = grp->second; + } + + return ret; +} + +void +IsoGrpOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + return; + } + + string table_name = consumer.getTableName(); + if (table_name == APP_ISOLATION_GROUP_TABLE_NAME) + { + doIsoGrpTblTask(consumer); + } + else + { + SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); + } +} + +void +IsoGrpOrch::doIsoGrpTblTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + isolation_group_status_t status = ISO_GRP_STATUS_SUCCESS; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + string key = kfvKey(t); + + size_t sep_loc = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string name = key.substr(0, sep_loc); + + SWSS_LOG_DEBUG("Op:%s IsoGrp:%s", op.c_str(), name.c_str()); + + if (op == SET_COMMAND) + { + isolation_group_type_t type = ISOLATION_GROUP_TYPE_INVALID; + string descr(""); + string bind_ports(""); + string mem_ports(""); + + for (auto itp : kfvFieldsValues(t)) + { + string attr_name = to_upper(fvField(itp)); + string attr_value = fvValue(itp); + + if (attr_name == ISOLATION_GRP_DESCRIPTION) + { + descr = attr_value; + } + else if (attr_name == ISOLATION_GRP_TYPE) + { + if (ISOLATION_GRP_TYPE_PORT == attr_value) + { + type = ISOLATION_GROUP_TYPE_PORT; + } + else if (ISOLATION_GRP_TYPE_BRIDGE_PORT == attr_value) + { + type = ISOLATION_GROUP_TYPE_BRIDGE_PORT; + } + else + SWSS_LOG_WARN("Attr:%s unknown type:%d", attr_name.c_str(), type); + } + else if (attr_name == ISOLATION_GRP_PORTS) + { + bind_ports = attr_value; + } + else if (attr_name == ISOLATION_GRP_MEMBERS) + { + mem_ports = attr_value; + } + else + SWSS_LOG_WARN("unknown Attr:%s ", attr_name.c_str()); + } + + status = addIsolationGroup(name, type, descr, bind_ports, mem_ports); + if (ISO_GRP_STATUS_SUCCESS == status) + { + auto grp = getIsolationGroup(name); + IsolationGroupUpdate update = {grp.get(), true}; + grp->notifyObservers(SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, &update); + + grp->attach(this); + } + } + else + { + auto grp = getIsolationGroup(name); + if (grp) + { + grp->detach(this); + + /* Send a notification and see if observers want to detach */ + IsolationGroupUpdate update = {grp.get(), false}; + grp->notifyObservers(SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, &update); + + // Finally delete it if it + status = delIsolationGroup(name); + } + } + + if (status != ISO_GRP_STATUS_RETRY) + { + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } + } +} + +isolation_group_status_t +IsoGrpOrch::addIsolationGroup(string name, isolation_group_type_t type, string descr, string bindPorts, string memPorts) +{ + SWSS_LOG_ENTER(); + + isolation_group_status_t status = ISO_GRP_STATUS_SUCCESS; + + // Add Or Update + auto grp = getIsolationGroup(name); + if (!grp) + { + // Add Case + auto grp = make_shared(name, type, descr); + + status = grp->create(); + if (ISO_GRP_STATUS_SUCCESS != status) + { + return status; + } + grp->setMembers(memPorts); + grp->setBindPorts(bindPorts); + this->m_isolationGrps[name] = grp; + } + else if (grp->getType() == type) + { + grp->m_description = descr; + grp->setMembers(memPorts); + grp->setBindPorts(bindPorts); + } + else + { + SWSS_LOG_ERROR("Isolation group type update to %d not permitted", type); + status = ISO_GRP_STATUS_FAIL; + } + + return status; +} + +isolation_group_status_t +IsoGrpOrch::delIsolationGroup(string name) +{ + SWSS_LOG_ENTER(); + + auto grp = m_isolationGrps.find(name); + if (grp != m_isolationGrps.end()) + { + grp->second->destroy(); + m_isolationGrps.erase(name); + } + + return ISO_GRP_STATUS_SUCCESS; +} + + +void +IsoGrpOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + if (type != SUBJECT_TYPE_BRIDGE_PORT_CHANGE) + { + return; + } + + for (auto kv : m_isolationGrps) + { + kv.second->update(type, cntx); + } +} + + +isolation_group_status_t +IsolationGroup::create() +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + attr.id = SAI_ISOLATION_GROUP_ATTR_TYPE; + if (ISOLATION_GROUP_TYPE_BRIDGE_PORT == m_type) + { + attr.value.s32 = SAI_ISOLATION_GROUP_TYPE_BRIDGE_PORT; + } + else + { + attr.value.s32 = SAI_ISOLATION_GROUP_TYPE_PORT; + } + + sai_status_t status = sai_isolation_group_api->create_isolation_group(&m_oid, gSwitchId, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Error %d creating isolation group %s", status, m_name.c_str()); + return ISO_GRP_STATUS_FAIL; + } + else + { + SWSS_LOG_NOTICE("Isolation group %s has oid 0x%" PRIx64 , m_name.c_str(), m_oid); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::destroy() +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + // Remove all bindings + attr.value.oid = SAI_NULL_OBJECT_ID; + for (auto p : m_bind_ports) + { + Port port; + gPortsOrch->getPort(p, port); + if (ISOLATION_GROUP_TYPE_BRIDGE_PORT == m_type) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + if (SAI_STATUS_SUCCESS != sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr)) + { + SWSS_LOG_ERROR("Unable to del SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP from %s", p.c_str()); + } + else + { + SWSS_LOG_NOTICE("SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP removed from %s", p.c_str()); + } + } + else if (ISOLATION_GROUP_TYPE_PORT == m_type) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + if (SAI_STATUS_SUCCESS != sai_port_api->set_port_attribute( + (port.m_type == Port::PHY ? port.m_port_id : port.m_lag_id), + &attr)) + { + SWSS_LOG_ERROR("Unable to del SAI_PORT_ATTR_ISOLATION_GROUP from %s", p.c_str()); + } + else + { + SWSS_LOG_NOTICE("SAI_PORT_ATTR_ISOLATION_GROUP removed from %s", p.c_str()); + } + } + } + m_bind_ports.clear(); + m_pending_bind_ports.clear(); + + // Remove all members + for (auto &kv : m_members) + { + if (SAI_STATUS_SUCCESS != sai_isolation_group_api->remove_isolation_group_member(kv.second)) + { + SWSS_LOG_ERROR("Unable to delete isolation group member 0x%" PRIx64 " from %s: 0x%" PRIx64 " for port %s", + kv.second, + m_name.c_str(), + m_oid, + kv.first.c_str()); + } + else + { + SWSS_LOG_NOTICE("Isolation group member 0x%" PRIx64 " deleted from %s: 0x%" PRIx64 " for port %s", + kv.second, + m_name.c_str(), + m_oid, + kv.first.c_str()); + } + } + m_members.clear(); + + sai_status_t status = sai_isolation_group_api->remove_isolation_group(m_oid); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to delete isolation group %s with oid 0x%" PRIx64 , m_name.c_str(), m_oid); + } + else + { + SWSS_LOG_NOTICE("Isolation group %s with oid 0x%" PRIx64 " deleted", m_name.c_str(), m_oid); + } + m_oid = SAI_NULL_OBJECT_ID; + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::addMember(Port &port) +{ + SWSS_LOG_ENTER(); + sai_object_id_t port_id = SAI_NULL_OBJECT_ID; + + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + port_id = port.m_bridge_port_id; + } + else if (m_type == ISOLATION_GROUP_TYPE_PORT) + { + port_id = (port.m_type == Port::PHY ? port.m_port_id : port.m_lag_id); + } + + if (SAI_NULL_OBJECT_ID == port_id) + { + SWSS_LOG_NOTICE("Port %s not ready for for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + + m_pending_members.push_back(port.m_alias); + + return ISO_GRP_STATUS_SUCCESS; + } + + if (m_members.find(port.m_alias) != m_members.end()) + { + SWSS_LOG_DEBUG("Port %s: 0x%" PRIx64 "already a member of %s", port.m_alias.c_str(), port_id, m_name.c_str()); + } + else + { + sai_object_id_t mem_id = SAI_NULL_OBJECT_ID; + sai_attribute_t mem_attr[2]; + sai_status_t status = SAI_STATUS_SUCCESS; + + mem_attr[0].id = SAI_ISOLATION_GROUP_MEMBER_ATTR_ISOLATION_GROUP_ID; + mem_attr[0].value.oid = m_oid; + mem_attr[1].id = SAI_ISOLATION_GROUP_MEMBER_ATTR_ISOLATION_OBJECT; + mem_attr[1].value.oid = port_id; + + status = sai_isolation_group_api->create_isolation_group_member(&mem_id, gSwitchId, 2, mem_attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to add %s: 0x%" PRIx64 " as member of %s:0x%" PRIx64 , port.m_alias.c_str(), port_id, + m_name.c_str(), m_oid); + return ISO_GRP_STATUS_FAIL; + } + else + { + m_members[port.m_alias] = mem_id; + SWSS_LOG_NOTICE("Port %s: 0x%" PRIx64 " added as member of %s: 0x%" PRIx64 "with oid 0x%" PRIx64, + port.m_alias.c_str(), + port_id, + m_name.c_str(), + m_oid, + mem_id); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::delMember(Port &port, bool do_fwd_ref) +{ + SWSS_LOG_ENTER(); + + if (m_members.find(port.m_alias) == m_members.end()) + { + auto node = find(m_pending_members.begin(), m_pending_members.end(), port.m_alias); + if (node != m_pending_members.end()) + { + m_pending_members.erase(node); + } + + return ISO_GRP_STATUS_SUCCESS; + } + + sai_object_id_t mem_id = m_members[port.m_alias]; + sai_status_t status = SAI_STATUS_SUCCESS; + + status = sai_isolation_group_api->remove_isolation_group_member(mem_id); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to delete isolation group member 0x%" PRIx64 " for port %s and iso group %s 0x%" PRIx64 , + mem_id, + port.m_alias.c_str(), + m_name.c_str(), + m_oid); + + return ISO_GRP_STATUS_FAIL; + } + else + { + SWSS_LOG_NOTICE("Deleted isolation group member 0x%" PRIx64 "for port %s and iso group %s 0x%" PRIx64 , + mem_id, + port.m_alias.c_str(), + m_name.c_str(), + m_oid); + + m_members.erase(port.m_alias); + } + + if (do_fwd_ref) + { + m_pending_members.push_back(port.m_alias); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::setMembers(string ports) +{ + SWSS_LOG_ENTER(); + auto port_list = tokenize(ports, ','); + set portList(port_list.begin(), port_list.end()); + vector old_members = m_pending_members; + + for (auto mem : m_members) + { + old_members.emplace_back(mem.first); + } + + for (auto alias : portList) + { + if ((0 == alias.find("Ethernet")) || (0 == alias.find("PortChannel"))) + { + auto iter = find(old_members.begin(), old_members.end(), alias); + if (iter != old_members.end()) + { + SWSS_LOG_NOTICE("Port %s already part of %s. No change", alias.c_str(), m_name.c_str()); + old_members.erase(iter); + } + else + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_NOTICE("Port %s not found. Added it to m_pending_members", alias.c_str()); + m_pending_members.emplace_back(alias); + continue; + } + addMember(port); + } + } + else + { + SWSS_LOG_ERROR("Port %s not supported", alias.c_str()); + continue; + } + } + + // Remove all the ports which are no longer needed + for (auto alias : old_members) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Port %s not found", alias.c_str()); + m_pending_members.erase(find(m_pending_members.begin(), m_pending_members.end(), port.m_alias)); + } + else + { + delMember(port); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::bind(Port &port) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias) != m_bind_ports.end()) + { + SWSS_LOG_NOTICE("isolation group %s of type %d already bound to Port %s", + m_name.c_str(), + m_type, + port.m_alias.c_str()); + + return ISO_GRP_STATUS_SUCCESS; + } + + attr.value.oid = m_oid; + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + if (port.m_bridge_port_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", + attr.id, + attr.value.oid, + port.m_alias.c_str()); + } + else + { + m_bind_ports.push_back(port.m_alias); + } + } + else + { + m_pending_bind_ports.push_back(port.m_alias); + SWSS_LOG_NOTICE("Port %s saved in pending bind ports for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + } + } + else if ((m_type == ISOLATION_GROUP_TYPE_PORT) && (port.m_type == Port::PHY)) + { + if (port.m_port_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", + attr.id, + attr.value.oid, + port.m_alias.c_str()); + } + else + { + m_bind_ports.push_back(port.m_alias); + } + } + else + { + m_pending_bind_ports.push_back(port.m_alias); + SWSS_LOG_NOTICE("Port %s saved in pending bind ports for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + } + } + else + { + SWSS_LOG_ERROR("Invalid attribute type %d ", m_type); + return ISO_GRP_STATUS_INVALID_PARAM; + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::unbind(Port &port, bool do_fwd_ref) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias) == m_bind_ports.end()) + { + auto node = find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias); + if (node != m_pending_bind_ports.end()) + { + m_pending_bind_ports.erase(node); + } + + return ISO_GRP_STATUS_SUCCESS; + } + + attr.value.oid = SAI_NULL_OBJECT_ID; + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + } + else if ((m_type == ISOLATION_GROUP_TYPE_PORT) && (port.m_type == Port::PHY)) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + } + else + { + return ISO_GRP_STATUS_INVALID_PARAM; + } + + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", attr.id, attr.value.oid, port.m_alias.c_str()); + } + else + { + m_bind_ports.erase(find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias)); + } + + if (do_fwd_ref) + { + m_pending_bind_ports.push_back(port.m_alias); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::setBindPorts(string ports) +{ + SWSS_LOG_ENTER(); + vector old_bindports = m_pending_bind_ports; + auto port_list = tokenize(ports, ','); + set portList(port_list.begin(), port_list.end()); + + old_bindports.insert(old_bindports.end(), m_bind_ports.begin(), m_bind_ports.end()); + for (auto alias : portList) + { + if ((0 == alias.find("Ethernet")) || (0 == alias.find("PortChannel"))) + { + auto iter = find(old_bindports.begin(), old_bindports.end(), alias); + if (iter != old_bindports.end()) + { + SWSS_LOG_NOTICE("%s is already bound to %s", m_name.c_str(), alias.c_str()); + old_bindports.erase(iter); + } + else + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_NOTICE("Port %s not found. Added it to m_pending_bind_ports", alias.c_str()); + m_pending_bind_ports.emplace_back(alias); + return ISO_GRP_STATUS_INVALID_PARAM; + } + bind(port); + } + } + else + { + return ISO_GRP_STATUS_INVALID_PARAM; + } + } + + // Remove all the ports which are no longer needed + for (auto alias : old_bindports) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Port %s not found", alias.c_str()); + m_pending_bind_ports.erase(find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias)); + } + else + { + unbind(port); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +void +IsolationGroup::update(SubjectType, void *cntx) +{ + PortUpdate *update = static_cast(cntx); + Port &port = update->port; + + if (update->add) + { + auto mem_node = find(m_pending_members.begin(), m_pending_members.end(), port.m_alias); + if (mem_node != m_pending_members.end()) + { + m_pending_members.erase(mem_node); + addMember(port); + } + + auto bind_node = find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias); + if (bind_node != m_pending_bind_ports.end()) + { + m_pending_bind_ports.erase(bind_node); + bind(port); + } + } + else + { + auto bind_node = find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias); + if (bind_node != m_bind_ports.end()) + { + unbind(port, true); + } + + auto mem_node = m_members.find(port.m_alias); + if (mem_node != m_members.end()) + { + delMember(port, true); + } + } +} diff --git a/orchagent/isolationgrouporch.h b/orchagent/isolationgrouporch.h new file mode 100644 index 0000000000..7bf8ef4c14 --- /dev/null +++ b/orchagent/isolationgrouporch.h @@ -0,0 +1,142 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ISOLATIONGROUPORCH_H__ +#define __ISOLATIONGROUPORCH_H__ + +#include "orch.h" +#include "port.h" +#include "observer.h" + +#define ISOLATION_GRP_DESCRIPTION "DESCRIPTION" +#define ISOLATION_GRP_TYPE "TYPE" +#define ISOLATION_GRP_PORTS "PORTS" +#define ISOLATION_GRP_MEMBERS "MEMBERS" +#define ISOLATION_GRP_TYPE_PORT "port" +#define ISOLATION_GRP_TYPE_BRIDGE_PORT "bridge-port" + +typedef enum IsolationGroupType +{ + ISOLATION_GROUP_TYPE_INVALID, + ISOLATION_GROUP_TYPE_PORT, + ISOLATION_GROUP_TYPE_BRIDGE_PORT +} isolation_group_type_t; + +typedef enum IsolationGroupStatus +{ + ISO_GRP_STATUS_RETRY = -100, + ISO_GRP_STATUS_FAIL, + ISO_GRP_STATUS_INVALID_PARAM, + ISO_GRP_STATUS_SUCCESS = 0 +} isolation_group_status_t; + +class IsolationGroup: public Observer, public Subject +{ +public: + string m_description; + + IsolationGroup(string name, isolation_group_type_t type = ISOLATION_GROUP_TYPE_PORT, string description=""): + m_name(name), + m_description(description), + m_type(type), + m_oid(SAI_NULL_OBJECT_ID) + { + } + + // Create Isolation group in SAI + isolation_group_status_t create(); + + // Delete Isolation group in SAI + isolation_group_status_t destroy(); + + // Add Isolation group member + isolation_group_status_t addMember(Port &port); + + // Delete Isolation group member + isolation_group_status_t delMember(Port &port, bool do_fwd_ref=false); + + // Set Isolation group members to the input. May involve adding or deleting members + isolation_group_status_t setMembers(string ports); + + // Apply the Isolation group to all linked ports + isolation_group_status_t bind(Port &port); + + // Remove the Isolation group from all linked ports + isolation_group_status_t unbind(Port &port, bool do_fwd_ref=false); + + // Set Isolation group binding to the input. May involve bind + isolation_group_status_t setBindPorts(string ports); + + void update(SubjectType, void *); + + isolation_group_type_t + getType() + { + return m_type; + } + + void notifyObservers(SubjectType type, void *cntx) + { + this->notify(type, cntx); + } + +protected: + string m_name; + isolation_group_type_t m_type; + sai_object_id_t m_oid; + map m_members; // Members Name -> Member OID + vector m_bind_ports; // Ports in which this Iso Group is applied. + vector m_pending_members; + vector m_pending_bind_ports; +}; + +class IsoGrpOrch : public Orch, public Observer +{ +public: + IsoGrpOrch(vector &connectors); + + ~IsoGrpOrch(); + + shared_ptr + getIsolationGroup(string name); + + isolation_group_status_t + addIsolationGroup(string name, isolation_group_type_t type, string descr, string bindPorts, string memPorts); + + isolation_group_status_t + delIsolationGroup(string name); + + void update(SubjectType, void *); + +private: + void + doTask(Consumer &consumer); + + void + doIsoGrpTblTask(Consumer &consumer); + + map> m_isolationGrps; +}; + +struct IsolationGroupUpdate +{ + IsolationGroup *group; + bool add; +}; + + +#endif /* __ISOLATIONGROUPORCH_H__ */ diff --git a/orchagent/mlagorch.cpp b/orchagent/mlagorch.cpp new file mode 100644 index 0000000000..81fd169fe4 --- /dev/null +++ b/orchagent/mlagorch.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "portsorch.h" +#include "mlagorch.h" + +using namespace std; +using namespace swss; + +extern PortsOrch *gPortsOrch; +extern MlagOrch *gMlagOrch; + +MlagOrch::MlagOrch(DBConnector *db, vector &tableNames): + Orch(db, tableNames) +{ + SWSS_LOG_ENTER(); +} + +MlagOrch::~MlagOrch() +{ + SWSS_LOG_ENTER(); +} + +void MlagOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); +} +//------------------------------------------------------------------ +//Private API section +//------------------------------------------------------------------ +void MlagOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + return; + } + string table_name = consumer.getTableName(); + if (table_name == CFG_MCLAG_TABLE_NAME) + { + doMlagDomainTask(consumer); + } + else if (table_name == CFG_MCLAG_INTF_TABLE_NAME) + { + doMlagInterfaceTask(consumer); + } + else + { + SWSS_LOG_ERROR("MLAG receives invalid table %s", table_name.c_str()); + } +} + +//Only interest in peer-link info from MLAG domain table +void MlagOrch::doMlagDomainTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string peer_link; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "peer_link") + { + peer_link = fvValue(i); + break; + } + } + if (!peer_link.empty()) + { + if (addIslInterface(peer_link)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + if (delIslInterface()) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("MLAG receives unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +//MLAG interface table key format: MCLAG_INTF_TABLE|mclag|ifname +void MlagOrch::doMlagInterfaceTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + size_t delimiter_pos; + string mlag_if_name; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + string key = kfvKey(t); + + delimiter_pos = key.find_first_of("|"); + mlag_if_name = key.substr(delimiter_pos+1); + + if (op == SET_COMMAND) + { + if (addMlagInterface(mlag_if_name)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (delMlagInterface(mlag_if_name)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("MLAG receives unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +bool MlagOrch::addIslInterface(string isl_name) +{ + Port isl_port; + MlagIslUpdate update; + + //No change + if ((m_isl_name == isl_name) || (isl_name.empty())) + return true; + + m_isl_name = isl_name; + + //Update observers + update.isl_name = isl_name; + update.is_add = true; + notify(SUBJECT_TYPE_MLAG_ISL_CHANGE, static_cast(&update)); + return true; +} + +bool MlagOrch::delIslInterface() +{ + MlagIslUpdate update; + + if (m_isl_name.empty()) + return true; + + update.isl_name = m_isl_name; + update.is_add = false; + + m_isl_name.clear(); + + //Notify observer + notify(SUBJECT_TYPE_MLAG_ISL_CHANGE, static_cast(&update)); + + return true; +} + +//Mlag interface can be added even before interface is configured +bool MlagOrch::addMlagInterface(string if_name) +{ + MlagIfUpdate update; + + //Duplicate add + if (m_mlagIntfs.find(if_name) != m_mlagIntfs.end()) + { + SWSS_LOG_ERROR("MLAG adds duplicate MLAG interface %s", if_name.c_str()); + } + else + { + + m_mlagIntfs.insert(if_name); + + //Notify observer + update.if_name = if_name; + update.is_add = true; + notify(SUBJECT_TYPE_MLAG_INTF_CHANGE, static_cast(&update)); + } + return true; + +} +bool MlagOrch::delMlagInterface(string if_name) +{ + MlagIfUpdate update; + + //Delete an unknown MLAG interface + if (m_mlagIntfs.find(if_name) == m_mlagIntfs.end()) + { + SWSS_LOG_ERROR("MLAG deletes unknown MLAG interface %s", if_name.c_str()); + } + else + { + m_mlagIntfs.erase(if_name); + + //Notify observers + update.if_name = if_name; + update.is_add = false; + notify(SUBJECT_TYPE_MLAG_INTF_CHANGE, static_cast(&update)); + } + return true; +} + +bool MlagOrch::isMlagInterface(string if_name) +{ + if (m_mlagIntfs.find(if_name) == m_mlagIntfs.end()) + return false; + else + return true; +} + +bool MlagOrch::isIslInterface(string if_name) +{ + if (m_isl_name == if_name) + return true; + else + return false; +} diff --git a/orchagent/mlagorch.h b/orchagent/mlagorch.h new file mode 100644 index 0000000000..27f4e9219b --- /dev/null +++ b/orchagent/mlagorch.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SWSS_MLAGORCH_H +#define SWSS_MLAGORCH_H + +#include +#include +#include "orch.h" +#include "port.h" + +struct MlagIfUpdate +{ + string if_name; + bool is_add; +}; + +struct MlagIslUpdate +{ + string isl_name; + bool is_add; +}; + +class MlagOrch: public Orch, public Observer, public Subject +{ +public: + MlagOrch(DBConnector *db, vector &tableNames); + ~MlagOrch(); + void update(SubjectType type, void *cntx); + bool isMlagInterface(string if_name); + bool isIslInterface(string if_name); + + const std::set& + getMlagIntfs() const + { + return m_mlagIntfs; + } + +private: + std::string m_isl_name; + std::set m_mlagIntfs; + + void doTask(Consumer &consumer); + void doMlagDomainTask(Consumer &consumer); + void doMlagInterfaceTask(Consumer &consumer); + bool addIslInterface(string isl_name); + bool delIslInterface(); + bool addMlagInterface(string if_name); + bool delMlagInterface(string if_name); +}; + +#endif /* SWSS_MLAGORCH_H */ diff --git a/orchagent/observer.h b/orchagent/observer.h index 76f00f1bfd..03668c1198 100644 --- a/orchagent/observer.h +++ b/orchagent/observer.h @@ -16,8 +16,14 @@ enum SubjectType SUBJECT_TYPE_MIRROR_SESSION_CHANGE, SUBJECT_TYPE_INT_SESSION_CHANGE, SUBJECT_TYPE_PORT_CHANGE, + SUBJECT_TYPE_BRIDGE_PORT_CHANGE, SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, - SUBJECT_TYPE_FDB_FLUSH_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_MEMBER_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_BINDING_CHANGE, + SUBJECT_TYPE_MLAG_INTF_CHANGE, + SUBJECT_TYPE_MLAG_ISL_CHANGE, + SUBJECT_TYPE_FDB_FLUSH_CHANGE }; class Observer diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 673c5f71ec..a546ffdac1 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -39,6 +39,8 @@ BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; +MlagOrch *gMlagOrch; +IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; bool gIsNatSupported = false; @@ -104,13 +106,15 @@ bool OrchDaemon::init() vector app_fdb_tables = { { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, FdbOrch::fdborch_pri} }; gCrmOrch = new CrmOrch(m_configDb, CFG_CRM_TABLE_NAME); gPortsOrch = new PortsOrch(m_applDb, m_stateDb, ports_tables, m_chassisAppDb); TableConnector stateDbFdb(m_stateDb, STATE_FDB_TABLE_NAME); - gFdbOrch = new FdbOrch(m_applDb, app_fdb_tables, stateDbFdb, gPortsOrch); + TableConnector stateMclagDbFdb(m_stateDb, STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + gFdbOrch = new FdbOrch(m_applDb, app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, @@ -326,6 +330,19 @@ bool OrchDaemon::init() } gAclOrch = new AclOrch(acl_table_connectors, gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, dtel_orch); + vector mlag_tables = { + { CFG_MCLAG_TABLE_NAME }, + { CFG_MCLAG_INTF_TABLE_NAME } + }; + gMlagOrch = new MlagOrch(m_configDb, mlag_tables); + + TableConnector appDbIsoGrpTbl(m_applDb, APP_ISOLATION_GROUP_TABLE_NAME); + vector iso_grp_tbl_ctrs = { + appDbIsoGrpTbl + }; + + gIsoGrpOrch = new IsoGrpOrch(iso_grp_tbl_ctrs); + m_orchList.push_back(gFdbOrch); m_orchList.push_back(gMirrorOrch); m_orchList.push_back(gAclOrch); @@ -340,6 +357,8 @@ bool OrchDaemon::init() m_orchList.push_back(vnet_orch); m_orchList.push_back(vnet_rt_orch); m_orchList.push_back(gNatOrch); + m_orchList.push_back(gMlagOrch); + m_orchList.push_back(gIsoGrpOrch); m_orchList.push_back(gFgNhgOrch); m_orchList.push_back(mux_orch); m_orchList.push_back(mux_cb_orch); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 1829414265..bdcc053ed3 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -32,6 +32,8 @@ #include "debugcounterorch.h" #include "directory.h" #include "natorch.h" +#include "isolationgrouporch.h" +#include "mlagorch.h" #include "muxorch.h" #include "macsecorch.h" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 4ac36258b9..bf8b1ed557 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -4245,6 +4245,9 @@ bool PortsOrch::addBridgePort(Port &port) m_portList[port.m_alias] = port; SWSS_LOG_NOTICE("Add bridge port %s to default 1Q bridge", port.m_alias.c_str()); + PortUpdate update = { port, true }; + notify(SUBJECT_TYPE_BRIDGE_PORT_CHANGE, static_cast(&update)); + return true; } @@ -4298,6 +4301,10 @@ bool PortsOrch::removeBridgePort(Port &port) } port.m_bridge_port_id = SAI_NULL_OBJECT_ID; + /* Remove bridge port */ + PortUpdate update = { port, false }; + notify(SUBJECT_TYPE_BRIDGE_PORT_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Remove bridge port %s from default 1Q bridge", port.m_alias.c_str()); m_portList[port.m_alias] = port; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 15febdb518..561fd5df8d 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -62,6 +62,7 @@ sai_dtel_api_t* sai_dtel_api; sai_samplepacket_api_t* sai_samplepacket_api; sai_debug_counter_api_t* sai_debug_counter_api; sai_nat_api_t* sai_nat_api; +sai_isolation_group_api_t* sai_isolation_group_api; sai_system_port_api_t* sai_system_port_api; sai_macsec_api_t* sai_macsec_api; @@ -183,6 +184,7 @@ void initSaiApi() sai_api_query(SAI_API_SAMPLEPACKET, (void **)&sai_samplepacket_api); sai_api_query(SAI_API_DEBUG_COUNTER, (void **)&sai_debug_counter_api); sai_api_query(SAI_API_NAT, (void **)&sai_nat_api); + sai_api_query(SAI_API_ISOLATION_GROUP, (void **)&sai_isolation_group_api); sai_api_query(SAI_API_SYSTEM_PORT, (void **)&sai_system_port_api); sai_api_query(SAI_API_MACSEC, (void **)&sai_macsec_api); diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 30ab875f5d..69f25a6b0e 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -69,6 +69,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/debugcounterorch.cpp \ $(top_srcdir)/orchagent/natorch.cpp \ $(top_srcdir)/orchagent/muxorch.cpp \ + $(top_srcdir)/orchagent/mlagorch.cpp \ + $(top_srcdir)/orchagent/isolationgrouporch.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ $(top_srcdir)/orchagent/lagid.cpp diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 5d5fe5de64..4b1ce101ae 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -330,16 +330,18 @@ namespace aclorch_test ASSERT_EQ(gIntfsOrch, nullptr); gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); - TableConnector applDbFdb(m_app_db.get(), APP_FDB_TABLE_NAME); - TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + const int fdborch_pri = 20; vector app_fdb_tables = { { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri} }; - + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); ASSERT_EQ(gFdbOrch, nullptr); - gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, gPortsOrch); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); ASSERT_EQ(gNeighOrch, nullptr); gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); diff --git a/tests/test_mclag_cfg.py b/tests/test_mclag_cfg.py new file mode 100644 index 0000000000..0a79c767da --- /dev/null +++ b/tests/test_mclag_cfg.py @@ -0,0 +1,238 @@ +# Common file to test all MCLAG related changes +from swsscommon import swsscommon +import time +import re +import json +import pytest +import platform +from distutils.version import StrictVersion + + +def delete_table_keys(db, table): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + for key in keys: + tbl.delete(key) + +#check table entry exits with this key +def check_table_exists(db, table, key): + error_info = [ ] + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + if key not in keys: + error_info.append("The table with desired key %s not found" % key) + return False, error_info + return True, error_info + +#check table entry doesn't exits with this key +def check_table_doesnt_exists(db, table, key): + error_info = [ ] + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + if key in keys: + error_info.append("unexcpected: The table with desired key %s is found" % key) + return False, error_info + return True, error_info + + + + +# Test MCLAG Configs +class TestMclagConfig(object): + CFG_MCLAG_DOMAIN_TABLE = "MCLAG_DOMAIN" + CFG_MCLAG_INTERFACE_TABLE = "MCLAG_INTERFACE" + + PORTCHANNEL1 = "PortChannel11" + PORTCHANNEL2 = "PortChannel50" + PORTCHANNEL3 = "PortChannel51" + + MCLAG_DOMAIN_ID = "4095" + MCLAG_SRC_IP = "10.5.1.1" + MCLAG_PEER_IP = "10.5.1.2" + MCLAG_PEER_LINK = PORTCHANNEL1 + + MCLAG_DOMAIN_2 = "111" + + MCLAG_SESS_TMOUT_VALID_LIST = ["3","3600"] + MCLAG_KA_VALID_LIST = ["1","60"] + + MCLAG_KA_INVALID_LIST = ["0","61"] + MCLAG_SESS_TMOUT_INVALID_LIST = ["0","3601"] + + MCLAG_INTERFACE1 = PORTCHANNEL2 + MCLAG_INTERFACE2 = PORTCHANNEL3 + + + # Testcase 1 Verify Configuration of MCLAG Domain with src, peer ip and peer link config gets updated in CONFIG_DB + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + #cleanup existing entries + delete_table_keys(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE) + delete_table_keys(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE) + + cmd_string ="config mclag add {} {} {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_SRC_IP, self.MCLAG_PEER_IP, self.MCLAG_PEER_LINK) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK) + ] + ) + assert ok,error_info + + # Testcase 2 Verify that second domain addition fails when there is already a domain configured + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_add_2nd(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + cmd_string ="config mclag add {} {} {} {}".format(self.MCLAG_DOMAIN_2, self.MCLAG_SRC_IP, self.MCLAG_PEER_IP, self.MCLAG_PEER_LINK) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether second domain config is not added to config db + key_string = self.MCLAG_DOMAIN_2 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, key_string) + assert ok,error_info + + + + # Testcase 3 Verify Configuration of MCLAG Interface to existing domain + @pytest.mark.dev_sanity + def test_mclag_cfg_intf_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + cmd_string ="config mclag member add {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE1) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether mclag interface config is reflected + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE1 + ok,error_info = check_table_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + # Testcase 4 Verify remove and add mclag interface + @pytest.mark.dev_sanity + def test_mclag_cfg_intf_remove_and_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + cmd_string ="config mclag member del {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE1) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether mclag interface is removed + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE1 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + #add different mclag interface + cmd_string ="config mclag member del {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE2) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether new mclag interface is added + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE2 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + # Testcase 5 Verify Configuration of valid values for session timeout + @pytest.mark.dev_sanity + def test_mclag_cfg_session_timeout_valid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_SESS_TMOUT_VALID_LIST: + cmd_string ="config mclag session-timeout {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK), + ("session_timeout",value) + ] + ) + assert ok,error_info + + # Testcase 6 Verify Configuration of valid values for KA timer + @pytest.mark.dev_sanity + def test_mclag_cfg_ka_valid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_KA_VALID_LIST: + cmd_string ="config mclag keepalive-interval {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK), + ("keepalive_interval",value) + ] + ) + assert ok,error_info + + + + # Testcase 7 Verify Configuration of invalid values for KA + @pytest.mark.dev_sanity + def test_mclag_cfg_ka_invalid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_KA_INVALID_LIST: + cmd_string ="config mclag keepalive-interval {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + found,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("keepalive_interval",value) + ] + ) + assert found == False, "invalid keepalive value %s written to CONFIG_DB" % value + + # Testcase 8 Verify Configuration of invalid values for session timeout + @pytest.mark.dev_sanity + def test_mclag_cfg_session_invalid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_SESS_TMOUT_INVALID_LIST: + cmd_string ="config mclag session-timeout {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + found,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("session_timeout",value) + ] + ) + assert found == False, "invalid keepalive value %s written to CONFIG_DB" % value + + # Testcase 9 Verify Deletion of MCLAG Domain + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_del(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + cmd_string ="config mclag del {}".format(self.MCLAG_DOMAIN_ID) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok, error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID) + assert ok,error_info + + #make sure mclag interface tables entries are also deleted when mclag domain is deleted + key_string = self.MCLAG_DOMAIN_ID + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + diff --git a/tests/test_mclag_fdb.py b/tests/test_mclag_fdb.py new file mode 100644 index 0000000000..5049859437 --- /dev/null +++ b/tests/test_mclag_fdb.py @@ -0,0 +1,505 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +import pytest +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + +def get_port_oid(dvs, port_name): + counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + port_map_tbl = swsscommon.Table(counters_db, 'COUNTERS_PORT_NAME_MAP') + for k in port_map_tbl.get('')[1]: + if k[0] == port_name: + return k[1] + return None + +def get_bridge_port_oid(dvs, port_oid): + tbl = swsscommon.Table(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + for key in tbl.getKeys(): + status, data = tbl.get(key) + assert status + values = dict(data) + if port_oid == values["SAI_BRIDGE_PORT_ATTR_PORT_ID"]: + return key + return None + +def create_port_channel(dvs, alias): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"), + ("mtu", "9100")]) + tbl.set(alias, fvs) + time.sleep(1) + +def remove_port_channel(dvs, alias): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL") + tbl._del(alias) + time.sleep(1) + +def add_port_channel_members(dvs, lag, members): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL_MEMBER") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + for member in members: + tbl.set(lag + "|" + member, fvs) + time.sleep(1) + +def remove_port_channel_members(dvs, lag, members): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL_MEMBER") + for member in members: + tbl._del(lag + "|" + member) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +# Test-1 Verify basic config add + +@pytest.mark.dev_sanity +def test_mclagFdb_basic_config_add(dvs, testlog): + dvs.setup_db() + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create PortChannel0005 + create_port_channel(dvs, "PortChannel0005") + + # add members to PortChannel0005 + add_port_channel_members(dvs, "PortChannel0005", ["Ethernet4"]) + + # create PortChannel0006 + create_port_channel(dvs, "PortChannel0006") + + # add members to PortChannel0006 + add_port_channel_members(dvs, "PortChannel0006", ["Ethernet8"]) + + # create PortChannel0008 + create_port_channel(dvs, "PortChannel0008") + + # add members to PortChannel0008 + add_port_channel_members(dvs, "PortChannel0008", ["Ethernet12"]) + + # create vlan + dvs.create_vlan("200") + + # Add vlan members + dvs.create_vlan_member("200", "PortChannel0005") + dvs.create_vlan_member("200", "PortChannel0006") + dvs.create_vlan_member("200", "PortChannel0008") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan200 wasn't created" + assert bp_after - bp_before == 3, "The bridge port wasn't created" + assert vm_after - vm_before == 3, "The vlan member wasn't added" + +# Test-2 Remote Dynamic MAC Add + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_dynamic_mac_add(dvs, testlog): + dvs.setup_db() + # create FDB entry in APP_DB MCLAG_FDB_TABLE + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + +# Test-3 Remote Dynamic MAC Delete + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_dynamic_mac_delete(dvs, testlog): + dvs.setup_db() + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG fdb entry not deleted" + + +# Test-4 Remote Static MAC Add + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_static_mac_add(dvs, testlog): + dvs.setup_db() + + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "static"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG static fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + +# Test-5 Remote Static MAC Del + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_static_mac_del(dvs, testlog): + dvs.setup_db() + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG static fdb entry not deleted" + + +# Test-6 Verify Remote to Local Move. + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_to_local_mac_move(dvs, testlog): + dvs.setup_db() + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #Move MAC to PortChannel0008 on Local FDB_TABLE + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + +# Test-7 Verify Remote MAC del should not del local MAC after move + +@pytest.mark.dev_sanity +def test_mclagFdb_local_mac_move_del(dvs, testlog): + dvs.setup_db() + + #Cleanup the FDB from MCLAG_FDB_TABLE + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + # Verify that the local FDB entry still inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + + + #delete the local FDB and Verify + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not deleted" + + +# Test-8 Verify Local to Remote Move. + +@pytest.mark.dev_sanity +def test_mclagFdb_local_to_remote_move(dvs, testlog): + dvs.setup_db() + + #Add local MAC to FDB_TABLE on PortChannel0008 + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + + #Move MAC to PortChannel0005 on Remote FDB_TABLE + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + +# Test-9 Verify local MAC del should not del remote MAC after move + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_move_del(dvs, testlog): + dvs.setup_db() + + #Cleanup the local FDB + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + # check that the remote FDB entry still inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #delete the remote FDB and Verify + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry not deleted" + + + +# Test-10 Verify remote MAC move in remote node is updated locally. + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_move_peer_node(dvs, testlog): + dvs.setup_db() + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + # Move remote MAC in MCLAG_FDB_TABLE to PortChannel0006 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0006"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #delete the remote FDB and Verify + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry not deleted" + + +# Test-11 Verify remote dynamic MAC move rejection in presecense of local static MAC. + +@pytest.mark.dev_sanity +def test_mclagFdb_static_mac_dynamic_move_reject(dvs, testlog): + dvs.setup_db() + + #Add local MAC to FDB_TABLE on PortChannel0008 + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "static"), + ] + ) + + # check that the static FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that still static FDB entry is inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG static fdb entry not deleted" + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + +# Test-12 Verify cleanup of the basic config. + +@pytest.mark.dev_sanity +def test_mclagFdb_basic_config_del(dvs, testlog): + dvs.setup_db() + + dvs.remove_vlan_member("200", "PortChannel0005") + dvs.remove_vlan_member("200", "PortChannel0006") + dvs.remove_vlan_member("200", "PortChannel0008") + dvs.remove_vlan("200") + remove_port_channel_members(dvs, "PortChannel0005", + ["Ethernet4"]) + remove_port_channel_members(dvs, "PortChannel0006", + ["Ethernet8"]) + remove_port_channel_members(dvs, "PortChannel0008", + ["Ethernet12"]) + remove_port_channel(dvs, "PortChannel0005") + remove_port_channel(dvs, "PortChannel0006") + remove_port_channel(dvs, "PortChannel0008") + From c458dbad13cc883e1c4152490c4f32aad6c633d0 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Wed, 4 Aug 2021 04:00:07 +0000 Subject: [PATCH 24/28] Addressing code review comments --- orchagent/portsorch.h | 2 +- orchagent/vxlanorch.cpp | 17 ++++++++--------- orchagent/vxlanorch.h | 4 ++++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index f598ab9058..93003f9078 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -222,7 +222,7 @@ class PortsOrch : public Orch, public Subject unordered_map m_portOidToIndex; map m_port_ref_count; unordered_set m_pendingPortSet; - uint32_t max_flood_control_types = 4; + const uint32_t max_flood_control_types = 4; set uuc_sup_flood_control_type; set bc_sup_flood_control_type; map m_bridge_port_ref_count; diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 3a7dec7132..555f852dec 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -909,8 +909,7 @@ void VxlanTunnel::deletePendingSIPTunnel() VxlanTunnelOrch* tunnel_orch = gDirectory.get(); bool dip_tunnels_used = tunnel_orch->dipTunnelsUsed(); - if (((dip_tunnels_used && getDipTunnelCnt() == 0) || - !dip_tunnels_used ) && del_tnl_hw_pending) + if ((!dip_tunnels_used || getDipTunnelCnt() == 0) && del_tnl_hw_pending) { uint8_t mapper_list=0; @@ -1071,7 +1070,7 @@ void VxlanTunnel::updateRemoteEndPointIpRef(const std::string remote_vtep, bool { if (it == tnl_users_.end()) { - memset(&tnl_refcnts,0,sizeof(tunnel_refcnt_t)); + memset(&tnl_refcnts, 0, sizeof(tunnel_refcnt_t)); tnl_refcnts.ip_refcnt++; tnl_users_[remote_vtep] = tnl_refcnts; } @@ -1637,11 +1636,11 @@ std::string VxlanTunnelOrch::getTunnelPortName(const std::string& vtep, bool loc std::string tunnelPortName; if (local) { - tunnelPortName = "Port_SRC_VTEP_" + vtep; + tunnelPortName = LOCAL_TUNNEL_PORT_PREFIX + vtep; } else { - tunnelPortName = "Port_EVPN_" + vtep; + tunnelPortName = EVPN_TUNNEL_PORT_PREFIX + vtep; } return tunnelPortName; } @@ -1649,14 +1648,14 @@ std::string VxlanTunnelOrch::getTunnelPortName(const std::string& vtep, bool loc bool VxlanTunnelOrch::isSrcVtepTunnel(Port& tunnelPort) { string tunnel_port_name = tunnelPort.m_alias; - string prefix = "Port_SRC_VTEP_"; + string prefix = LOCAL_TUNNEL_PORT_PREFIX; return (tunnel_port_name.compare(0, prefix.length(), prefix) == 0); } void VxlanTunnelOrch::getTunnelNameFromDIP(const string& dip, string& tunnel_name) { - tunnel_name = "EVPN_" + dip; + tunnel_name = EVPN_TUNNEL_NAME_PREFIX + dip; return; } @@ -1673,7 +1672,7 @@ void VxlanTunnelOrch::getTunnelNameFromPort(string& tunnel_portname, string& tun void VxlanTunnelOrch:: getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep) { remote_vtep = tunnelPort.m_alias; - remote_vtep.erase(0,sizeof("Port_EVPN_")-1); + remote_vtep.erase(0,sizeof(EVPN_TUNNEL_PORT_PREFIX)-1); } @@ -1766,7 +1765,7 @@ bool VxlanTunnel::isTunnelReferenced() if (!ret) { SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); - return false; + return false; } if (tunnelPort.m_fdb_count != 0) diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index c8a8cabd4f..56d5d41ac0 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -35,6 +35,10 @@ typedef enum #define IS_TUNNELMAP_SET_VRF(x) ((x)& (1< Date: Thu, 5 Aug 2021 08:13:47 -0700 Subject: [PATCH 25/28] Open record file in append mode (#1845) *Open record file (swss.rec) in append mode. Similar to sairedis.rec --- orchagent/orch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 2afc2c20f4..b4ef246662 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -536,7 +536,7 @@ void Orch::logfileReopen() * empty file here. */ - gRecordOfs.open(gRecordFile); + gRecordOfs.open(gRecordFile, std::ofstream::out | std::ofstream::app); if (!gRecordOfs.is_open()) { From 8f7ea146deda9a269505713eb117ca85a001bb29 Mon Sep 17 00:00:00 2001 From: Akhilesh Samineni <47657796+AkhileshSamineni@users.noreply.github.com> Date: Thu, 5 Aug 2021 23:49:44 +0530 Subject: [PATCH 26/28] Code changes to support IPv6 Link local enhancements (#1463) * Code changes to support IPv6 Link local enchancements - Changes to handle the "ipv6_use_link_local_only" option on a interface which is added/deleted based on Global Config or Interface config. - Adds/Deletes the RIF for an interface in the absence of IPv6 global address on a interface based on "ipv6_use_link_local_only". - Allow the ipv6 link-local address as neighbors. - Allow adding the IPv6 routes with link-local nexthops. - Skip Ipv4LL neighbor add in Orchagent for RFC5549. Signed-off-by: Akhilesh Samineni --- cfgmgr/intfmgr.cpp | 24 +++- neighsyncd/neighsync.cpp | 68 ++++++++++- neighsyncd/neighsync.h | 5 +- neighsyncd/neighsyncd.cpp | 3 +- orchagent/neighorch.cpp | 13 +++ orchagent/routeorch.cpp | 25 ++++- orchagent/routeorch.h | 7 +- tests/test_interface.py | 230 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 358 insertions(+), 17 deletions(-) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index b40f55692a..3e0ed862be 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -476,6 +476,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string proxy_arp = ""; string grat_arp = ""; string mpls = ""; + string ipv6_link_local_mode = ""; for (auto idx : data) { @@ -506,11 +507,14 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { mpls = value; } - - if (field == "nat_zone") + else if (field == "nat_zone") { nat_zone = value; } + else if (field == "ipv6_use_link_local_only") + { + ipv6_link_local_mode = value; + } } if (op == SET_COMMAND) @@ -551,6 +555,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("nat_zone", nat_zone); data.push_back(fvTuple); } + /* Set mpls */ if (!setIntfMpls(alias, mpls)) { @@ -562,6 +567,13 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("mpls", mpls); data.push_back(fvTuple); } + + /* Set ipv6 mode */ + if (!ipv6_link_local_mode.empty()) + { + FieldValueTuple fvTuple("ipv6_use_link_local_only", ipv6_link_local_mode); + data.push_back(fvTuple); + } } if (!parentAlias.empty()) @@ -731,8 +743,8 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, std::vector fvVector; FieldValueTuple f("family", ip_prefix.isV4() ? IPV4_NAME : IPV6_NAME); - // Don't send link local config to AppDB and Orchagent - if (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE) + // Don't send ipv4 link local config to AppDB and Orchagent + if ((ip_prefix.isV4() == false) || (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE)) { FieldValueTuple s("scope", "global"); fvVector.push_back(s); @@ -745,8 +757,8 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, { setIntfIp(alias, "del", ip_prefix); - // Don't send link local config to AppDB and Orchagent - if (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE) + // Don't send ipv4 link local config to AppDB and Orchagent + if ((ip_prefix.isV4() == false) || (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE)) { m_appIntfTableProducer.del(appKey); m_stateIntfTable.del(keys[0] + state_db_key_delimiter + keys[1]); diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 054f13a470..6b1abc235f 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -13,13 +13,17 @@ #include "neighsync.h" #include "warm_restart.h" +#include using namespace std; using namespace swss; -NeighSync::NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb) : +NeighSync::NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *cfgDb) : m_neighTable(pipelineAppDB, APP_NEIGH_TABLE_NAME), - m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME) + m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME), + m_cfgInterfaceTable(cfgDb, CFG_INTF_TABLE_NAME), + m_cfgLagInterfaceTable(cfgDb, CFG_LAG_INTF_TABLE_NAME), + m_cfgVlanInterfaceTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME) { m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "neighsyncd", "swss", DEFAULT_NEIGHSYNC_WARMSTART_TIMER); if (m_AppRestartAssist) @@ -57,6 +61,7 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; string key; string family; + string intfName; if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && (nlmsg_type != RTM_DELNEIGH)) @@ -70,12 +75,18 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) return; key+= LinkCache::getInstance().ifindexToName(rtnl_neigh_get_ifindex(neigh)); + intfName = key; key+= ":"; nl_addr2str(rtnl_neigh_get_dst(neigh), ipStr, MAX_ADDR_SIZE); - /* Ignore IPv6 link-local addresses as neighbors */ + /* Ignore IPv6 link-local addresses as neighbors, if ipv6 link local mode is disabled */ if (family == IPV6_NAME && IN6_IS_ADDR_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) - return; + { + if (isLinkLocalEnabled(intfName) == false) + { + return; + } + } /* Ignore IPv6 multicast link-local addresses as neighbors */ if (family == IPV6_NAME && IN6_IS_ADDR_MC_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) return; @@ -124,3 +135,52 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) m_neighTable.set(key, fvVector); } } + +/* To check the ipv6 link local is enabled on a given port */ +bool NeighSync::isLinkLocalEnabled(const string &port) +{ + vector values; + + if (!port.compare(0, strlen("Vlan"), "Vlan")) + { + if (!m_cfgVlanInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else if (!port.compare(0, strlen("PortChannel"), "PortChannel")) + { + if (!m_cfgLagInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else if (!port.compare(0, strlen("Ethernet"), "Ethernet")) + { + if (!m_cfgInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else + { + SWSS_LOG_INFO("IPv6 Link local is not supported for %s ", port.c_str()); + return false; + } + + auto it = std::find_if(values.begin(), values.end(), [](const FieldValueTuple& t){ return t.first == "ipv6_use_link_local_only";}); + if (it != values.end()) + { + if (it->second == "enable") + { + SWSS_LOG_INFO("IPv6 Link local is enabled on %s", port.c_str()); + return true; + } + } + + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; +} diff --git a/neighsyncd/neighsync.h b/neighsyncd/neighsync.h index 387c849f30..49a17ee6b6 100644 --- a/neighsyncd/neighsync.h +++ b/neighsyncd/neighsync.h @@ -23,7 +23,7 @@ class NeighSync : public NetMsg public: enum { MAX_ADDR_SIZE = 64 }; - NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb); + NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *cfgDb); ~NeighSync(); virtual void onMsg(int nlmsg_type, struct nl_object *obj); @@ -39,6 +39,9 @@ class NeighSync : public NetMsg Table m_stateNeighRestoreTable; ProducerStateTable m_neighTable; AppRestartAssist *m_AppRestartAssist; + Table m_cfgVlanInterfaceTable, m_cfgLagInterfaceTable, m_cfgInterfaceTable; + + bool isLinkLocalEnabled(const std::string &port); }; } diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index 99e86b2ef9..a0882c28e2 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -18,8 +18,9 @@ int main(int argc, char **argv) DBConnector appDb("APPL_DB", 0); RedisPipeline pipelineAppDB(&appDb); DBConnector stateDb("STATE_DB", 0); + DBConnector cfgDb("CONFIG_DB", 0); - NeighSync sync(&pipelineAppDB, &stateDb); + NeighSync sync(&pipelineAppDB, &stateDb, &cfgDb); NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index b9870a24eb..f792c24621 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -671,6 +671,19 @@ void NeighOrch::doTask(Consumer &consumer) IpAddress ip_address(key.substr(found+1)); + /* Verify Ipv4 LinkLocal and skip neighbor entry added for RFC5549 */ + if ((ip_address.getAddrScope() == IpAddress::LINK_SCOPE) && (ip_address.isV4())) + { + /* Check if this prefix is not a configured ip, if so allow */ + IpPrefix ipll_prefix(ip_address.getV4Addr(), 16); + if (!m_intfsOrch->isPrefixSubnet (ipll_prefix, alias)) + { + SWSS_LOG_NOTICE("Skip IPv4LL neighbor %s, Intf:%s op: %s ", ip_address.to_string().c_str(), alias.c_str(), op.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + NeighborEntry neighbor_entry = { ip_address, alias }; if (op == SET_COMMAND) diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 0f688a64d0..3cf490a5fc 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -127,15 +127,15 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, IpPrefix linklocal_prefix = getLinkLocalEui64Addr(); addLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); /* Add fe80::/10 subnet route to forward all link-local packets * destined to us, to CPU */ IpPrefix default_link_local_prefix("fe80::/10"); addLinkLocalRouteToMe(gVirtualRouterId, default_link_local_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", default_link_local_prefix.to_string().c_str()); - /* TODO: Add the link-local fe80::/10 route to cpu in every VRF created from - * vrforch::addOperation. */ } std::string RouteOrch::getLinkLocalEui64Addr(void) @@ -205,6 +205,27 @@ void RouteOrch::addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); } +void RouteOrch::delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix) +{ + sai_route_entry_t unicast_route_entry; + unicast_route_entry.switch_id = gSwitchId; + unicast_route_entry.vr_id = vrf_id; + copy(unicast_route_entry.destination, linklocal_prefix); + subnet(unicast_route_entry.destination, unicast_route_entry.destination); + + sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete link local ipv6 route %s to cpu, rv:%d", + linklocal_prefix.getIp().to_string().c_str(), status); + return; + } + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + + SWSS_LOG_NOTICE("Deleted link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); +} + bool RouteOrch::hasNextHopGroup(const NextHopGroupKey& nexthops) const { return m_syncdNextHopGroups.find(nexthops) != m_syncdNextHopGroups.end(); diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index aca68624f6..20e79699d5 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -152,6 +152,10 @@ class RouteOrch : public Orch, public Subject bool createFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id, vector &nhg_attrs); bool removeFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id); + void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); + void delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); + std::string getLinkLocalEui64Addr(void); + private: SwitchOrch *m_switchOrch; NeighOrch *m_neighOrch; @@ -188,9 +192,6 @@ class RouteOrch : public Orch, public Subject bool addLabelRoutePost(const LabelRouteBulkContext& ctx, const NextHopGroupKey &nextHops); bool removeLabelRoutePost(const LabelRouteBulkContext& ctx); - std::string getLinkLocalEui64Addr(void); - void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); - void doTask(Consumer& consumer); void doLabelTask(Consumer& consumer); }; diff --git a/tests/test_interface.py b/tests/test_interface.py index ac1446481a..a57970b1e5 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1964,6 +1964,236 @@ def test_LoopbackInterfaceIpv4AddressWithVrf(self, dvs, testlog): assert False + def create_ipv6_link_local(self, interface): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + + fvs = swsscommon.FieldValuePairs([("ipv6_use_link_local_only", "enable")]) + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl.set(interface, fvs) + time.sleep(1) + + def remove_ipv6_link_local(self, interface): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl._del(interface) + time.sleep(1) + + def test_InterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for physical interface + self.setup_db(dvs) + + # create ipv6 link local interface + self.create_ipv6_link_local("Ethernet8") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # bring up interface + self.set_admin_status(dvs, "Ethernet8", "up") + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # remove ipv6 link local interface + self.remove_ipv6_link_local("Ethernet8") + + # bring down interface + self.set_admin_status(dvs, "Ethernet8", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 1 + + def test_LagInterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for lag interface + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel001") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # create ipv6 link local interface + self.create_ipv6_link_local("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("PortChannel001") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # remove ipv6 link local interface + self.remove_ipv6_link_local("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 1 + + # remove port channel + self.remove_port_channel("PortChannel001") + + + def test_VLanInterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for vlan interface + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + # create ipv6 link local interface + self.create_ipv6_link_local("Vlan10") + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Vlan10") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + + + # remove ipv6 link local interface + self.remove_ipv6_link_local("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 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(): From df96059aba319410f60d752eff6211f3ba5cb347 Mon Sep 17 00:00:00 2001 From: minionatwork <60480922+minionatwork@users.noreply.github.com> Date: Thu, 5 Aug 2021 11:31:00 -0700 Subject: [PATCH 27/28] VOQ: Nexthop for remote VOQ LC should be created on inband OIF. (#1823) VOQ nexthop for remote neighbors should be created on local inband port only for the kernel purpose. SAI should use actual RIF of the remote system port interface. #1686 seems to be break this condition and this change address it. --- orchagent/neighorch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index f792c24621..e3f9a4175e 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -191,7 +191,7 @@ bool NeighOrch::addNextHop(const NextHopKey &nh) } NextHopKey nexthop(nh); - if (m_intfsOrch->isRemoteSystemPortIntf(nexthop.alias)) + if (m_intfsOrch->isRemoteSystemPortIntf(nh.alias)) { //For remote system ports kernel nexthops are always on inband. Change the key Port inbp; @@ -202,7 +202,7 @@ bool NeighOrch::addNextHop(const NextHopKey &nh) } assert(!hasNextHop(nexthop)); - sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(nexthop.alias); + sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(nh.alias); vector next_hop_attrs; From 67ca9cce4abf688821dbbde66cb44f3e5ab39a76 Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Fri, 6 Aug 2021 05:08:44 +0000 Subject: [PATCH 28/28] Addressing code review comments --- orchagent/vxlanorch.cpp | 21 +++++++------- orchagent/vxlanorch.h | 7 ++++- tests/evpn_tunnel.py | 36 +++++------------------- tests/test_evpn_fdb_p2mp.py | 16 ----------- tests/test_evpn_l3_vxlan.py | 48 ++++++++++++++++---------------- tests/test_evpn_l3_vxlan_p2mp.py | 48 ++++++++++++++++---------------- 6 files changed, 72 insertions(+), 104 deletions(-) diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 555f852dec..61d4c8229e 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -15,6 +15,7 @@ #include "swssnet.h" #include "warm_restart.h" #include "tokenize.h" +#include "converter.h" /* Global variables */ extern sai_object_id_t gSwitchId; @@ -1821,7 +1822,7 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) } auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -1954,7 +1955,7 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); bool ret; - ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); + ret = gPortsOrch->getPort(port_tunnel_name, tunnelPort); // If there are Dynamic DIP Tunnels referring to this SIP Tunnel // then mark it as pending for delete. if (!tunnel_obj->isTunnelReferenced()) @@ -2030,7 +2031,7 @@ bool VxlanVrfMapOrch::addOperation(const Request& request) } auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan vni id is too big: %d", vni_id); return true; @@ -2164,7 +2165,7 @@ bool EvpnRemoteVnip2pOrch::addOperation(const Request& request) sai_vlan_id_t vlan_id = (sai_vlan_id_t) stoi(vlan_name.substr(4)); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2233,7 +2234,7 @@ bool EvpnRemoteVnip2pOrch::delOperation(const Request& request) sai_vlan_id_t vlan_id = (sai_vlan_id_t)stoi(vlan_name.substr(4)); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2305,10 +2306,10 @@ bool EvpnRemoteVnip2mpOrch::addOperation(const Request& request) // Extract VLAN and VNI auto vlan_name = request.getKeyString(0); - sai_vlan_id_t vlan_id = (sai_vlan_id_t) stoi(vlan_name.substr(4)); + sai_vlan_id_t vlan_id = to_uint(vlan_name.substr(4), MIN_VLAN_ID, MAX_VLAN_ID); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2369,10 +2370,10 @@ bool EvpnRemoteVnip2mpOrch::delOperation(const Request& request) // Extract VLAN and VNI auto vlan_name = request.getKeyString(0); - sai_vlan_id_t vlan_id = (sai_vlan_id_t)stoi(vlan_name.substr(4)); + sai_vlan_id_t vlan_id = to_uint(vlan_name.substr(4), MIN_VLAN_ID, MAX_VLAN_ID); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2388,7 +2389,7 @@ bool EvpnRemoteVnip2mpOrch::delOperation(const Request& request) if (!vtep_ptr) { SWSS_LOG_WARN("Remote VNI add: VTEP not found. remote=%s vid=%d", - end_point_ip.c_str(),vlan_id); + end_point_ip.c_str(), vlan_id); return true; } diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index 56d5d41ac0..9d6b531eec 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -39,6 +39,11 @@ typedef enum #define EVPN_TUNNEL_PORT_PREFIX "Port_EVPN_" #define EVPN_TUNNEL_NAME_PREFIX "EVPN_" +#define MIN_VLAN_ID 1 +#define MAX_VLAN_ID 4095 + +#define MAX_VNI_ID (1<<24) + typedef enum { TNL_CREATION_SRC_CLI, @@ -175,7 +180,7 @@ class VxlanTunnel return ids_.tunnel_term_id; } - IpAddress getSrcIP() + const IpAddress getSrcIP() { return src_ip_; } diff --git a/tests/evpn_tunnel.py b/tests/evpn_tunnel.py index 1c1f41a2a2..48ed6c0904 100644 --- a/tests/evpn_tunnel.py +++ b/tests/evpn_tunnel.py @@ -39,7 +39,6 @@ def get_exist_entries(self, dvs, table): tbl = swsscommon.Table(db, table) return set(tbl.getKeys()) - def get_created_entry(self, db, table, existed_entries): tbl = swsscommon.Table(db, table) entries = set(tbl.getKeys()) @@ -84,7 +83,6 @@ def get_key_with_attr(self, db, table, expected_attributes ): tbl = swsscommon.Table(db, table) keys = tbl.getKeys() retkey = list() - #assert key in keys, "The desired key is not presented" for key in keys: status, fvs = tbl.get(key) @@ -109,22 +107,6 @@ def check_deleted_object(self, db, table, key): keys = tbl.getKeys() assert key not in keys, "The desired key is not removed" - -''' - def entries(self, db, table): - tbl = swsscommon.Table(self, db, table) - return set(tbl.getKeys()) - - def get_default_vr_id(self, dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] -''' - class VxlanTunnel(object): ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" @@ -187,7 +169,7 @@ def create_evpn_nvo(self, dvs, nvoname, tnl_name): def remove_evpn_nvo(self, dvs, nvoname): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - self.helper.delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) + self.helper.delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname) def create_vxlan_tunnel(self, dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) @@ -238,7 +220,7 @@ def remove_vxlan_tunnel(self, dvs, tnl_name): # create the VXLAN tunnel Term entry in Config DB self.helper.delete_entry_tbl( conf_db, - "VXLAN_TUNNEL", tnl_name, + "VXLAN_TUNNEL", tnl_name ) def remove_vxlan_tunnel_map(self, dvs, tnl_name, map_name,vni_id, vlan_id): @@ -252,7 +234,7 @@ def remove_vxlan_tunnel_map(self, dvs, tnl_name, map_name,vni_id, vlan_id): # create the VXLAN tunnel Term entry in Config DB self.helper.delete_entry_tbl( conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name) ) def remove_evpn_remote_vni(self, dvs, vlan_id, remote_vtep ): @@ -305,7 +287,7 @@ def create_vlan1(self, dvs, vlan_name): ], ) - def create_vrf_routes(self, dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): + def create_vrf_route(self, dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) attrs = [ @@ -327,14 +309,14 @@ def create_vrf_routes(self, dvs, prefix, vrf_name, endpoint, ifname, mac="", vni time.sleep(2) - def delete_vrf_routes(self, dvs, prefix, vrf_name): + def delete_vrf_route(self, dvs, prefix, vrf_name): app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) self.helper.delete_entry_pst(app_db, "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix)) time.sleep(2) - def create_vrf_routes_ecmp(self, dvs, prefix, vrf_name, ecmp_nexthop_attributes): + def create_vrf_route_ecmp(self, dvs, prefix, vrf_name, ecmp_nexthop_attributes): app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) self.helper.create_entry_pst( @@ -479,7 +461,6 @@ def fetch_exist_entries(self, dvs): if self.switch_mac is None: self.switch_mac = self.get_switch_mac(dvs) - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) @@ -514,8 +495,7 @@ def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] (exitcode, out) = dvs.runcmd(iplinkcmd) assert exitcode == 0, "Kernel device not created" - - + def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name, sip): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) @@ -758,8 +738,6 @@ def check_vlan_extension_p2mp(self, dvs, vlan_name, sip, dip): status, fvs = tbl.get(self.vlan_id_map[vlan_name]) print(fvs) - #assert 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE' in fvs, "Unknown unicast flood control type not set" - #assert 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE' in fvs, "Broadcast flood control type not set" uuc_flood_type = None bc_flood_type = None diff --git a/tests/test_evpn_fdb_p2mp.py b/tests/test_evpn_fdb_p2mp.py index 0aa8bd5ba0..6684245107 100644 --- a/tests/test_evpn_fdb_p2mp.py +++ b/tests/test_evpn_fdb_p2mp.py @@ -65,7 +65,6 @@ def test_evpnFdbP2MP(dvs, testlog): # create vlan print("Creating Vlan3") - #dvs.runcmd("config vlan add 3") dvs.create_vlan("3") time.sleep(2) @@ -82,7 +81,6 @@ def test_evpnFdbP2MP(dvs, testlog): vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") print("Making Ethernet0 as a member of Vlan3") - #dvs.runcmd("config vlan member add 3 Ethernet0") dvs.create_vlan_member("3", "Ethernet0") time.sleep(2) @@ -103,17 +101,14 @@ def test_evpnFdbP2MP(dvs, testlog): vxlan_obj.create_vxlan_tunnel(dvs, source_tnl_name, source_tnl_ip) time.sleep(1) - nvo_name = "evpn_nvo" vxlan_obj.create_evpn_nvo(dvs, nvo_name, source_tnl_name) time.sleep(1) - map_name_vlan_3 = "map_3_3" vxlan_obj.create_vxlan_tunnel_map(dvs, source_tnl_name, map_name_vlan_3, "3", "Vlan3") time.sleep(1) - remote_ip_6 = "6.6.6.6" vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_6, "3") remote_ip_8 = "8.8.8.8" @@ -188,7 +183,6 @@ def test_evpnFdbP2MP(dvs, testlog): ) time.sleep(1) - #raw_input("Check ASIC_DB.........") # check that the FDB entry was added in ASIC DB ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", @@ -273,8 +267,6 @@ def test_evpnFdbP2MP(dvs, testlog): assert ok, str(extra) print("EVPN Sticky FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") - #raw_input("Check ASIC_DB.........") - #UT-8 Evpn Mac add from remote when tunnels are already created mac = "52:54:00:25:06:E9" print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") @@ -289,8 +281,6 @@ def test_evpnFdbP2MP(dvs, testlog): ) time.sleep(1) - #raw_input("Check ASIC_DB.........") - # check that the FDB entry is inserted into ASIC DB ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", [("mac", mac), ("bvid", vlan_oid_3)], @@ -319,8 +309,6 @@ def test_evpnFdbP2MP(dvs, testlog): ) time.sleep(1) - #raw_input("Check ASIC_DB.........") - # check that the FDB entry is inserted into ASIC DB ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", [("mac", mac), ("bvid", vlan_oid_3)], @@ -347,8 +335,6 @@ def test_evpnFdbP2MP(dvs, testlog): time.sleep(2) - #raw_input("Check ASIC_DB.........") - print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") helper.create_entry_tbl( dvs.adb, @@ -366,8 +352,6 @@ def test_evpnFdbP2MP(dvs, testlog): time.sleep(2) - #raw_input("Check ASIC_DB.........") - # check that the FDB entry was added in ASIC DB ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_3)], diff --git a/tests/test_evpn_l3_vxlan.py b/tests/test_evpn_l3_vxlan.py index 6276830125..c7422fd6c1 100644 --- a/tests/test_evpn_l3_vxlan.py +++ b/tests/test_evpn_l3_vxlan.py @@ -157,14 +157,14 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTesting DIP tunnel 7.7.7.7 creation") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -270,22 +270,22 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -293,7 +293,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -308,7 +308,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) @@ -318,7 +318,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) @@ -328,7 +328,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') ecmp_nexthop_attr = [ @@ -339,7 +339,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") - vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -349,7 +349,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -360,7 +360,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -477,22 +477,22 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -500,7 +500,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -515,7 +515,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ] print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) @@ -525,7 +525,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) @@ -535,7 +535,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") @@ -546,7 +546,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), ] - vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -556,7 +556,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -567,7 +567,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') diff --git a/tests/test_evpn_l3_vxlan_p2mp.py b/tests/test_evpn_l3_vxlan_p2mp.py index cee794aedd..5edeb7d8f5 100644 --- a/tests/test_evpn_l3_vxlan_p2mp.py +++ b/tests/test_evpn_l3_vxlan_p2mp.py @@ -158,11 +158,11 @@ def test_prefix_route_create_remote_endpoint(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -262,22 +262,22 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -285,7 +285,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -300,7 +300,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) @@ -310,7 +310,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) @@ -320,7 +320,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') ecmp_nexthop_attr = [ @@ -331,7 +331,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") - vxlan_obj.create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -341,7 +341,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") - vxlan_obj.create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -352,7 +352,7 @@ def test_remote_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -465,22 +465,22 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -488,7 +488,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -503,7 +503,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): ] print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) @@ -513,7 +513,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) @@ -523,7 +523,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") @@ -534,7 +534,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), ] - vxlan_obj.create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -544,7 +544,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") - vxlan_obj.create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -555,7 +555,7 @@ def test_remote_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - vxlan_obj.delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED')