From 8119ec0f3d555c0c8538badf0ffe298bd654198c Mon Sep 17 00:00:00 2001 From: Shi Su <67605788+shi-su@users.noreply.github.com> Date: Wed, 10 Nov 2021 09:16:24 -0800 Subject: [PATCH] [bfdorch] Orchagent support hardware BFD (#1883) What I did Implement bfdorch to program hardware BFD sessions via bfd SAI. Add vs test for bfd sessions. Why I did it To support hardware BFD. How I verified it Configure hardware BFD sessions on virtual switches and physical devices and confirm the BFD session is programmed. --- orchagent/Makefile.am | 3 +- orchagent/bfdorch.cpp | 442 +++++++++++++++++++++++++++++++++ orchagent/bfdorch.h | 37 +++ orchagent/main.cpp | 4 + orchagent/notifications.cpp | 6 + orchagent/notifications.h | 1 + orchagent/observer.h | 3 +- orchagent/orchdaemon.cpp | 6 +- orchagent/orchdaemon.h | 1 + orchagent/saihelper.cpp | 3 + tests/mock_tests/Makefile.am | 3 +- tests/test_bfd.py | 466 +++++++++++++++++++++++++++++++++++ 12 files changed, 971 insertions(+), 4 deletions(-) create mode 100644 orchagent/bfdorch.cpp create mode 100644 orchagent/bfdorch.h create mode 100644 tests/test_bfd.py diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 0636f1ca72..bc58bbbbf9 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -81,7 +81,8 @@ orchagent_SOURCES = \ isolationgrouporch.cpp \ muxorch.cpp \ macsecorch.cpp \ - lagid.cpp + lagid.cpp \ + bfdorch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/bfdorch.cpp b/orchagent/bfdorch.cpp new file mode 100644 index 0000000000..68295842b3 --- /dev/null +++ b/orchagent/bfdorch.cpp @@ -0,0 +1,442 @@ +#include "bfdorch.h" +#include "intfsorch.h" +#include "vrforch.h" +#include "converter.h" +#include "swssnet.h" +#include "notifier.h" +#include "sai_serialize.h" +#include "directory.h" + +using namespace std; +using namespace swss; + +#define BFD_SESSION_DEFAULT_TX_INTERVAL 1000 +#define BFD_SESSION_DEFAULT_RX_INTERVAL 1000 +#define BFD_SESSION_DEFAULT_DETECT_MULTIPLIER 3 +#define BFD_SESSION_MILLISECOND_TO_MICROSECOND 1000 +#define BFD_SRCPORTINIT 49152 +#define BFD_SRCPORTMAX 65536 + +extern sai_bfd_api_t* sai_bfd_api; +extern sai_object_id_t gSwitchId; +extern sai_object_id_t gVirtualRouterId; +extern PortsOrch* gPortsOrch; +extern Directory gDirectory; + +const map session_type_map = +{ + {"demand_active", SAI_BFD_SESSION_TYPE_DEMAND_ACTIVE}, + {"demand_passive", SAI_BFD_SESSION_TYPE_DEMAND_PASSIVE}, + {"async_active", SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE}, + {"async_passive", SAI_BFD_SESSION_TYPE_ASYNC_PASSIVE} +}; + +const map session_type_lookup = +{ + {SAI_BFD_SESSION_TYPE_DEMAND_ACTIVE, "demand_active"}, + {SAI_BFD_SESSION_TYPE_DEMAND_PASSIVE, "demand_passive"}, + {SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE, "async_active"}, + {SAI_BFD_SESSION_TYPE_ASYNC_PASSIVE, "async_passive"} +}; + +const map session_state_lookup = +{ + {SAI_BFD_SESSION_STATE_ADMIN_DOWN, "Admin_Down"}, + {SAI_BFD_SESSION_STATE_DOWN, "Down"}, + {SAI_BFD_SESSION_STATE_INIT, "Init"}, + {SAI_BFD_SESSION_STATE_UP, "Up"} +}; + +BfdOrch::BfdOrch(DBConnector *db, string tableName, TableConnector stateDbBfdSessionTable): + Orch(db, tableName), + m_stateBfdSessionTable(stateDbBfdSessionTable.first, stateDbBfdSessionTable.second) +{ + SWSS_LOG_ENTER(); + + DBConnector *notificationsDb = new DBConnector("ASIC_DB", 0); + m_bfdStateNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); + auto bfdStateNotificatier = new Notifier(m_bfdStateNotificationConsumer, this, "BFD_STATE_NOTIFICATIONS"); + Orch::addExecutor(bfdStateNotificatier); +} + +BfdOrch::~BfdOrch(void) +{ + SWSS_LOG_ENTER(); +} + +void BfdOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + auto data = kfvFieldsValues(t); + + if (op == SET_COMMAND) + { + if (!create_bfd_session(key, data)) + { + it++; + continue; + } + } + else if (op == DEL_COMMAND) + { + if (!remove_bfd_session(key)) + { + it++; + continue; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + } + + it = consumer.m_toSync.erase(it); + } +} + +void BfdOrch::doTask(NotificationConsumer &consumer) +{ + SWSS_LOG_ENTER(); + + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + if (&consumer != m_bfdStateNotificationConsumer) + { + return; + } + + if (op == "bfd_session_state_change") + { + uint32_t count; + sai_bfd_session_state_notification_t *bfdSessionState = nullptr; + + sai_deserialize_bfd_session_state_ntf(data, count, &bfdSessionState); + + for (uint32_t i = 0; i < count; i++) + { + sai_object_id_t id = bfdSessionState[i].bfd_session_id; + sai_bfd_session_state_t state = bfdSessionState[i].session_state; + + SWSS_LOG_INFO("Get BFD session state change notification id:%" PRIx64 " state: %s", id, session_state_lookup.at(state).c_str()); + + if (state != bfd_session_lookup[id].state) + { + auto key = bfd_session_lookup[id].peer; + m_stateBfdSessionTable.hset(key, "state", session_state_lookup.at(state)); + + SWSS_LOG_NOTICE("BFD session state for %s changed from %s to %s", key.c_str(), + session_state_lookup.at(bfd_session_lookup[id].state).c_str(), session_state_lookup.at(state).c_str()); + + BfdUpdate update; + update.peer = key; + update.state = state; + notify(SUBJECT_TYPE_BFD_SESSION_STATE_CHANGE, static_cast(&update)); + + bfd_session_lookup[id].state = state; + } + } + + sai_deserialize_free_bfd_session_state_ntf(count, bfdSessionState); + } +} + +bool BfdOrch::create_bfd_session(const string& key, const vector& data) +{ + if (bfd_session_map.find(key) != bfd_session_map.end()) + { + SWSS_LOG_ERROR("BFD session for %s already exists", key.c_str()); + return true; + } + + size_t found_vrf = key.find(delimiter); + if (found_vrf == string::npos) + { + SWSS_LOG_ERROR("Failed to parse key %s, no vrf is given", key.c_str()); + return true; + } + + size_t found_ifname = key.find(delimiter, found_vrf + 1); + if (found_ifname == string::npos) + { + SWSS_LOG_ERROR("Failed to parse key %s, no ifname is given", key.c_str()); + return true; + } + + string vrf_name = key.substr(0, found_vrf); + string alias = key.substr(found_vrf + 1, found_ifname - found_vrf - 1); + IpAddress peer_address(key.substr(found_ifname + 1)); + + sai_bfd_session_type_t bfd_session_type = SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE; + sai_bfd_encapsulation_type_t encapsulation_type = SAI_BFD_ENCAPSULATION_TYPE_NONE; + IpAddress src_ip; + uint32_t tx_interval = BFD_SESSION_DEFAULT_TX_INTERVAL; + uint32_t rx_interval = BFD_SESSION_DEFAULT_RX_INTERVAL; + uint8_t multiplier = BFD_SESSION_DEFAULT_DETECT_MULTIPLIER; + bool multihop = false; + MacAddress dst_mac; + bool dst_mac_provided = false; + bool src_ip_provided = false; + + sai_attribute_t attr; + vector attrs; + vector fvVector; + + for (auto i : data) + { + auto value = fvValue(i); + + if (fvField(i) == "tx_interval") + { + tx_interval = to_uint(value); + } + else if (fvField(i) == "rx_interval") + { + rx_interval = to_uint(value); + } + else if (fvField(i) == "multiplier") + { + multiplier = to_uint(value); + } + else if (fvField(i) == "multihop") + { + multihop = (value == "true") ? true : false; + } + else if (fvField(i) == "local_addr") + { + src_ip = IpAddress(value); + src_ip_provided = true; + } + else if (fvField(i) == "type") + { + if (session_type_map.find(value) == session_type_map.end()) + { + SWSS_LOG_ERROR("Invalid BFD session type %s\n", value.c_str()); + continue; + } + bfd_session_type = session_type_map.at(value); + } + else if (fvField(i) == "dst_mac") + { + dst_mac = MacAddress(value); + dst_mac_provided = true; + } + else + SWSS_LOG_ERROR("Unsupported BFD attribute %s\n", fvField(i).c_str()); + } + + if (!src_ip_provided) + { + SWSS_LOG_ERROR("Failed to create BFD session %s because source IP is not provided", key.c_str()); + return true; + } + + attr.id = SAI_BFD_SESSION_ATTR_TYPE; + attr.value.s32 = bfd_session_type; + attrs.emplace_back(attr); + fvVector.emplace_back("type", session_type_lookup.at(bfd_session_type)); + + attr.id = SAI_BFD_SESSION_ATTR_LOCAL_DISCRIMINATOR; + attr.value.u32 = bfd_gen_id(); + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_UDP_SRC_PORT; + attr.value.u32 = bfd_src_port(); + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_REMOTE_DISCRIMINATOR; + attr.value.u32 = 0; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_BFD_ENCAPSULATION_TYPE; + attr.value.s32 = encapsulation_type; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_IPHDR_VERSION; + attr.value.u8 = src_ip.isV4() ? 4 : 6; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS; + copy(attr.value.ipaddr, src_ip); + attrs.emplace_back(attr); + fvVector.emplace_back("local_addr", src_ip.to_string()); + + attr.id = SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS; + copy(attr.value.ipaddr, peer_address); + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_MIN_TX; + attr.value.u32 = tx_interval * BFD_SESSION_MILLISECOND_TO_MICROSECOND; + attrs.emplace_back(attr); + fvVector.emplace_back("tx_interval", to_string(tx_interval)); + + attr.id = SAI_BFD_SESSION_ATTR_MIN_RX; + attr.value.u32 = rx_interval * BFD_SESSION_MILLISECOND_TO_MICROSECOND; + attrs.emplace_back(attr); + fvVector.emplace_back("rx_interval", to_string(rx_interval)); + + attr.id = SAI_BFD_SESSION_ATTR_MULTIPLIER; + attr.value.u8 = multiplier; + attrs.emplace_back(attr); + fvVector.emplace_back("multiplier", to_string(multiplier)); + + if (multihop) + { + attr.id = SAI_BFD_SESSION_ATTR_MULTIHOP; + attr.value.booldata = true; + attrs.emplace_back(attr); + fvVector.emplace_back("multihop", "true"); + } + else + { + fvVector.emplace_back("multihop", "false"); + } + + if (alias != "default") + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Failed to locate port %s", alias.c_str()); + return false; + } + + if (!dst_mac_provided) + { + SWSS_LOG_ERROR("Failed to create BFD session %s: destination MAC address required when hardware lookup not valid", + key.c_str()); + return true; + } + + if (vrf_name != "default") + { + SWSS_LOG_ERROR("Failed to create BFD session %s: vrf is not supported when hardware lookup not valid", + key.c_str()); + return true; + } + + attr.id = SAI_BFD_SESSION_ATTR_HW_LOOKUP_VALID; + attr.value.booldata = false; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_PORT; + attr.value.oid = port.m_port_id; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_SRC_MAC_ADDRESS; + memcpy(attr.value.mac, port.m_mac.getMac(), sizeof(sai_mac_t)); + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_DST_MAC_ADDRESS; + memcpy(attr.value.mac, dst_mac.getMac(), sizeof(sai_mac_t)); + attrs.emplace_back(attr); + } + else + { + if (dst_mac_provided) + { + SWSS_LOG_ERROR("Failed to create BFD session %s: destination MAC address not supported when hardware lookup valid", + key.c_str()); + return true; + } + + attr.id = SAI_BFD_SESSION_ATTR_VIRTUAL_ROUTER; + if (vrf_name == "default") + { + attr.value.oid = gVirtualRouterId; + } + else + { + VRFOrch* vrf_orch = gDirectory.get(); + attr.value.oid = vrf_orch->getVRFid(vrf_name); + } + + attrs.emplace_back(attr); + } + + fvVector.emplace_back("state", session_state_lookup.at(SAI_BFD_SESSION_STATE_DOWN)); + + sai_object_id_t bfd_session_id = SAI_NULL_OBJECT_ID; + sai_status_t status = sai_bfd_api->create_bfd_session(&bfd_session_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create bfd session %s, rv:%d", key.c_str(), status); + task_process_status handle_status = handleSaiCreateStatus(SAI_API_BFD, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + const string state_db_key = get_state_db_key(vrf_name, alias, peer_address); + m_stateBfdSessionTable.set(state_db_key, fvVector); + bfd_session_map[key] = bfd_session_id; + bfd_session_lookup[bfd_session_id] = {state_db_key, SAI_BFD_SESSION_STATE_DOWN}; + + BfdUpdate update; + update.peer = state_db_key; + update.state = SAI_BFD_SESSION_STATE_DOWN; + notify(SUBJECT_TYPE_BFD_SESSION_STATE_CHANGE, static_cast(&update)); + + return true; +} + +bool BfdOrch::remove_bfd_session(const string& key) +{ + if (bfd_session_map.find(key) == bfd_session_map.end()) + { + SWSS_LOG_ERROR("BFD session for %s does not exist", key.c_str()); + return true; + } + + sai_object_id_t bfd_session_id = bfd_session_map[key]; + sai_status_t status = sai_bfd_api->remove_bfd_session(bfd_session_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove bfd session %s, rv:%d", key.c_str(), status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_BFD, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + m_stateBfdSessionTable.del(bfd_session_lookup[bfd_session_id].peer); + bfd_session_map.erase(key); + bfd_session_lookup.erase(bfd_session_id); + + return true; +} + +string BfdOrch::get_state_db_key(const string& vrf_name, const string& alias, const IpAddress& peer_address) +{ + return vrf_name + state_db_key_delimiter + alias + state_db_key_delimiter + peer_address.to_string(); +} + +uint32_t BfdOrch::bfd_gen_id(void) +{ + static uint32_t session_id = 1; + return (session_id++); +} + +uint32_t BfdOrch::bfd_src_port(void) +{ + static uint32_t port = BFD_SRCPORTINIT; + if (port >= BFD_SRCPORTMAX) + { + port = BFD_SRCPORTINIT; + } + + return (port++); +} diff --git a/orchagent/bfdorch.h b/orchagent/bfdorch.h new file mode 100644 index 0000000000..6be1f8deae --- /dev/null +++ b/orchagent/bfdorch.h @@ -0,0 +1,37 @@ +#ifndef SWSS_BFDORCH_H +#define SWSS_BFDORCH_H + +#include "orch.h" +#include "observer.h" + +struct BfdUpdate +{ + std::string peer; + sai_bfd_session_state_t state; +}; + +class BfdOrch: public Orch, public Subject +{ +public: + void doTask(Consumer &consumer); + void doTask(swss::NotificationConsumer &consumer); + BfdOrch(swss::DBConnector *db, std::string tableName, TableConnector stateDbBfdSessionTable); + virtual ~BfdOrch(void); + +private: + bool create_bfd_session(const std::string& key, const std::vector& data); + bool remove_bfd_session(const std::string& key); + std::string get_state_db_key(const std::string& vrf_name, const std::string& alias, const swss::IpAddress& peer_address); + + uint32_t bfd_gen_id(void); + uint32_t bfd_src_port(void); + + std::map bfd_session_map; + std::map bfd_session_lookup; + + swss::Table m_stateBfdSessionTable; + + swss::NotificationConsumer* m_bfdStateNotificationConsumer; +}; + +#endif /* SWSS_BFDORCH_H */ diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 10dd8cb788..aacae5fe97 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -451,6 +451,10 @@ int main(int argc, char **argv) attr.value.ptr = (void *)on_port_state_change; attrs.push_back(attr); + attr.id = SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY; + attr.value.ptr = (void *)on_bfd_session_state_change; + attrs.push_back(attr); + attr.id = SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY; attr.value.ptr = (void *)on_switch_shutdown_request; attrs.push_back(attr); diff --git a/orchagent/notifications.cpp b/orchagent/notifications.cpp index 209c03d83b..1a49526370 100644 --- a/orchagent/notifications.cpp +++ b/orchagent/notifications.cpp @@ -17,6 +17,12 @@ void on_port_state_change(uint32_t count, sai_port_oper_status_notification_t *d // which causes concurrency access to the DB } +void on_bfd_session_state_change(uint32_t count, sai_bfd_session_state_notification_t *data) +{ + // don't use this event handler, because it runs by libsairedis in a separate thread + // which causes concurrency access to the DB +} + void on_switch_shutdown_request() { SWSS_LOG_ENTER(); diff --git a/orchagent/notifications.h b/orchagent/notifications.h index 2ee207ebfa..ea22593a1f 100644 --- a/orchagent/notifications.h +++ b/orchagent/notifications.h @@ -6,4 +6,5 @@ extern "C" { void on_fdb_event(uint32_t count, sai_fdb_event_notification_data_t *data); void on_port_state_change(uint32_t count, sai_port_oper_status_notification_t *data); +void on_bfd_session_state_change(uint32_t count, sai_bfd_session_state_notification_t *data); void on_switch_shutdown_request(); diff --git a/orchagent/observer.h b/orchagent/observer.h index 03668c1198..20945f6a61 100644 --- a/orchagent/observer.h +++ b/orchagent/observer.h @@ -23,7 +23,8 @@ enum SubjectType SUBJECT_TYPE_ISOLATION_GROUP_BINDING_CHANGE, SUBJECT_TYPE_MLAG_INTF_CHANGE, SUBJECT_TYPE_MLAG_ISL_CHANGE, - SUBJECT_TYPE_FDB_FLUSH_CHANGE + SUBJECT_TYPE_FDB_FLUSH_CHANGE, + SUBJECT_TYPE_BFD_SESSION_STATE_CHANGE }; class Observer diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index f4ef7b137c..9ef6cb07f3 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -44,6 +44,7 @@ NatOrch *gNatOrch; MlagOrch *gMlagOrch; IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; +BfdOrch *gBfdOrch; bool gIsNatSupported = false; @@ -290,6 +291,9 @@ bool OrchDaemon::init() gMacsecOrch = new MACsecOrch(m_applDb, m_stateDb, macsec_app_tables, gPortsOrch); + TableConnector stateDbBfdSessionTable(m_stateDb, STATE_BFD_SESSION_TABLE_NAME); + gBfdOrch = new BfdOrch(m_applDb, APP_BFD_SESSION_TABLE_NAME, stateDbBfdSessionTable); + /* * The order of the orch list is important for state restore of warm start and * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. @@ -298,7 +302,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgOrch, gRouteOrch, copp_orch, qos_orch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgOrch, gRouteOrch, copp_orch, qos_orch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch, gBfdOrch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 1d1d9f77cf..2e88447ce7 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -38,6 +38,7 @@ #include "mlagorch.h" #include "muxorch.h" #include "macsecorch.h" +#include "bfdorch.h" using namespace swss; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index ce8f7c0a27..c1c2c0b8ea 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -67,6 +67,7 @@ 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; sai_l2mc_group_api_t* sai_l2mc_group_api; +sai_bfd_api_t* sai_bfd_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -191,6 +192,7 @@ void initSaiApi() 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_api_query(SAI_API_BFD, (void **)&sai_bfd_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -224,6 +226,7 @@ void initSaiApi() 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); + sai_log_set(SAI_API_BFD, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location, const std::string &record_filename) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 96f936a622..c75a9ad3ca 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -77,7 +77,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/mlagorch.cpp \ $(top_srcdir)/orchagent/isolationgrouporch.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ - $(top_srcdir)/orchagent/lagid.cpp + $(top_srcdir)/orchagent/lagid.cpp \ + $(top_srcdir)/orchagent/bfdorch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/test_bfd.py b/tests/test_bfd.py new file mode 100644 index 0000000000..0e8b167360 --- /dev/null +++ b/tests/test_bfd.py @@ -0,0 +1,466 @@ +import pytest +import time + +from swsscommon import swsscommon + +class TestBfd(object): + def setup_db(self, dvs): + dvs.setup_db() + self.pdb = dvs.get_app_db() + self.adb = dvs.get_asic_db() + self.sdb = dvs.get_state_db() + + def get_exist_bfd_session(self): + return set(self.adb.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION")) + + def create_bfd_session(self, key, pairs): + tbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "BFD_SESSION_TABLE") + fvs = swsscommon.FieldValuePairs(list(pairs.items())) + tbl.set(key, fvs) + + def remove_bfd_session(self, key): + tbl = swsscommon.ProducerStateTable(self.pdb.db_connection, "BFD_SESSION_TABLE") + tbl._del(key) + + def check_asic_bfd_session_value(self, key, expected_values): + fvs = self.adb.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", key) + for k, v in expected_values.items(): + assert fvs[k] == v + + def check_state_bfd_session_value(self, key, expected_values): + fvs = self.sdb.get_entry("BFD_SESSION_TABLE", key) + for k, v in expected_values.items(): + assert fvs[k] == v + + def update_bfd_session_state(self, dvs, session, state): + bfd_sai_state = {"Admin_Down": "SAI_BFD_SESSION_STATE_ADMIN_DOWN", + "Down": "SAI_BFD_SESSION_STATE_DOWN", + "Init": "SAI_BFD_SESSION_STATE_INIT", + "Up": "SAI_BFD_SESSION_STATE_UP"} + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"bfd_session_id\":\""+session+"\",\"session_state\":\""+bfd_sai_state[state]+"\"}]" + ntf.send("bfd_session_state_change", ntf_data, fvp) + + def test_addRemoveBfdSession(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_ipv6(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "2000::1"} + self.create_bfd_session("default:default:2000::2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "2000::1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "2000::2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "6" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "2000::1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value("default|default|2000::2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Init") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Init" + self.check_state_bfd_session_value("default|default|2000::2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:2000::2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_interface(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1", "dst_mac": "00:02:03:04:05:06"} + self.create_bfd_session("default:Ethernet0:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_BFD_SESSION_ATTR_DST_MAC_ADDRESS": "00:02:03:04:05:06" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value("default|Ethernet0|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Down") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Down" + self.check_state_bfd_session_value("default|Ethernet0|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:Ethernet0:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_txrx_interval(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1", "tx_interval": "300", "rx_interval": "500"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_MIN_TX": "300000", + "SAI_BFD_SESSION_ATTR_MIN_RX": "500000", + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"300", + "rx_interval" : "500", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Admin_Down") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Admin_Down" + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_multiplier(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1", "multiplier": "5"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_MULTIPLIER": "5" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "5", "multihop": "false"} + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_multihop(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1", "multihop": "true"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_MULTIHOP": "true" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "true"} + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_addRemoveBfdSession_type(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session + fieldValues = {"local_addr": "10.0.0.1", "type": "demand_active"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked created BFD session in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_DEMAND_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4" + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + # Check STATE_DB entry related to the BFD session + expected_sdb_values = {"state": "Down", "type": "demand_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Send BFD session state notification to update BFD session state + self.update_bfd_session_state(dvs, session, "Up") + time.sleep(2) + + # Confirm BFD session state in STATE_DB is updated as expected + expected_sdb_values["state"] = "Up" + self.check_state_bfd_session_value("default|default|10.0.0.2", expected_sdb_values) + + # Remove the BFD session + self.remove_bfd_session("default:default:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_multipleBfdSessions(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + # Create BFD session 1 + key1 = "default:default:10.0.0.2" + fieldValues = {"local_addr": "10.0.0.1"} + self.create_bfd_session(key1, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked BFD session 1 in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + bfdSessions = self.get_exist_bfd_session() + assert len(createdSessions) == 1 + + session1 = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4" + } + self.check_asic_bfd_session_value(session1, expected_adb_values) + + # Check STATE_DB entry related to the BFD session 1 + key_state_db1 = "default|default|10.0.0.2" + expected_sdb_values1 = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value(key_state_db1, expected_sdb_values1) + + # Create BFD session 2 + key2 = "default:default:10.0.1.2" + fieldValues = {"local_addr": "10.0.0.1", "tx_interval": "300", "rx_interval": "500"} + self.create_bfd_session(key2, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked BFD session 2 in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + bfdSessions = self.get_exist_bfd_session() + assert len(createdSessions) == 1 + + session2 = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.1.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_MIN_TX": "300000", + "SAI_BFD_SESSION_ATTR_MIN_RX": "500000", + } + self.check_asic_bfd_session_value(session2, expected_adb_values) + + # Check STATE_DB entry related to the BFD session 2 + key_state_db2 = "default|default|10.0.1.2" + expected_sdb_values2 = {"state": "Down", "type": "async_active", "local_addr" : "10.0.0.1", "tx_interval" :"300", + "rx_interval" : "500", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value(key_state_db2, expected_sdb_values2) + + # Create BFD session 3 + key3 = "default:default:2000::2" + fieldValues = {"local_addr": "2000::1", "type": "demand_active"} + self.create_bfd_session(key3, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked BFD session 3 in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + bfdSessions = self.get_exist_bfd_session() + assert len(createdSessions) == 1 + + session3 = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "2000::1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "2000::2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_DEMAND_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "6" + } + self.check_asic_bfd_session_value(session3, expected_adb_values) + + # Check STATE_DB entry related to the BFD session 3 + key_state_db3 = "default|default|2000::2" + expected_sdb_values3 = {"state": "Down", "type": "demand_active", "local_addr" : "2000::1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value(key_state_db3, expected_sdb_values3) + + # Create BFD session 4 + key4 = "default:default:3000::2" + fieldValues = {"local_addr": "3000::1"} + self.create_bfd_session(key4, fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + # Checked BFD session 4 in ASIC_DB + createdSessions = self.get_exist_bfd_session() - bfdSessions + bfdSessions = self.get_exist_bfd_session() + assert len(createdSessions) == 1 + + session4 = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "3000::1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "3000::2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "6" + } + self.check_asic_bfd_session_value(session4, expected_adb_values) + + # Check STATE_DB entry related to the BFD session 4 + key_state_db4 = "default|default|3000::2" + expected_sdb_values4 = {"state": "Down", "type": "async_active", "local_addr" : "3000::1", "tx_interval" :"1000", + "rx_interval" : "1000", "multiplier" : "3", "multihop": "false"} + self.check_state_bfd_session_value(key_state_db4, expected_sdb_values4) + + # Update BFD session states + self.update_bfd_session_state(dvs, session1, "Up") + expected_sdb_values1["state"] = "Up" + self.update_bfd_session_state(dvs, session3, "Init") + expected_sdb_values3["state"] = "Init" + self.update_bfd_session_state(dvs, session4, "Admin_Down") + expected_sdb_values4["state"] = "Admin_Down" + time.sleep(2) + + # Confirm BFD session states in STATE_DB are updated as expected + self.check_state_bfd_session_value(key_state_db1, expected_sdb_values1) + self.check_state_bfd_session_value(key_state_db2, expected_sdb_values2) + self.check_state_bfd_session_value(key_state_db3, expected_sdb_values3) + self.check_state_bfd_session_value(key_state_db4, expected_sdb_values4) + + # Remove the BFD sessions + self.remove_bfd_session(key1) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session1) + self.remove_bfd_session(key2) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session2) + self.remove_bfd_session(key3) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session3) + self.remove_bfd_session(key4) + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session4)