From 81cc4b6d07a19651bbf2ad7216f215a442b0582e Mon Sep 17 00:00:00 2001 From: Lawrence Lee Date: Tue, 27 Feb 2024 15:01:20 -0800 Subject: [PATCH] Fix multi VLAN neighbor learning (#3049) What I did When adding a new neighbor, check if the neighbor IP has already been learned on a different VLAN. If it has, remove the old neighbor entry before adding the new one. Why I did it On Gemini devices, if a neighbor IP moves from an active port in one VLAN to a second VLAN, then back to the first VLAN (with 3 different MAC addresses), orchagent will crash. Even though the MAC address of the last move is different from the first MAC address, orchagent believes the last MAC address to already be programmed in the hardware and tries to set an attribute of the entry which doesn't exist. --- orchagent/neighorch.cpp | 21 ++ orchagent/portsorch.cpp | 7 + orchagent/portsorch.h | 4 + tests/mock_tests/Makefile.am | 2 + tests/mock_tests/flowcounterrouteorch_ut.cpp | 1 + tests/mock_tests/mock_orch_test.h | 305 +++++++++++++++++ tests/mock_tests/mock_sai_api.cpp | 25 ++ tests/mock_tests/mock_sai_api.h | 70 ++-- tests/mock_tests/mux_rollback_ut.cpp | 343 +++---------------- tests/mock_tests/neighorch_ut.cpp | 198 +++++++++++ tests/mock_tests/routeorch_ut.cpp | 1 + 11 files changed, 643 insertions(+), 334 deletions(-) create mode 100644 tests/mock_tests/mock_orch_test.h create mode 100644 tests/mock_tests/mock_sai_api.cpp create mode 100644 tests/mock_tests/neighorch_ut.cpp diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index efbe7ff481..4008fb81fb 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -933,6 +933,27 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress } } + PortsOrch* ports_orch = gDirectory.get(); + auto vlan_ports = ports_orch->getAllVlans(); + + for (auto vlan_port: vlan_ports) + { + if (vlan_port == alias) + { + continue; + } + NeighborEntry temp_entry = { ip_address, vlan_port }; + if (m_syncdNeighbors.find(temp_entry) != m_syncdNeighbors.end()) + { + SWSS_LOG_NOTICE("Neighbor %s on %s already exists, removing before adding new neighbor", ip_address.to_string().c_str(), vlan_port.c_str()); + if (!removeNeighbor(temp_entry)) + { + SWSS_LOG_ERROR("Failed to remove neighbor %s on %s", ip_address.to_string().c_str(), vlan_port.c_str()); + return false; + } + } + } + MuxOrch* mux_orch = gDirectory.get(); bool hw_config = isHwConfigured(neighborEntry); diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 9f9e22339f..69be94d325 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -1140,6 +1140,11 @@ map& PortsOrch::getAllPorts() return m_portList; } +unordered_set& PortsOrch::getAllVlans() +{ + return m_vlanPorts; +} + bool PortsOrch::getPort(string alias, Port &p) { SWSS_LOG_ENTER(); @@ -5727,6 +5732,7 @@ bool PortsOrch::addVlan(string vlan_alias) m_portList[vlan_alias] = vlan; m_port_ref_count[vlan_alias] = 0; saiOidToAlias[vlan_oid] = vlan_alias; + m_vlanPorts.emplace(vlan_alias); return true; } @@ -5793,6 +5799,7 @@ bool PortsOrch::removeVlan(Port vlan) saiOidToAlias.erase(vlan.m_vlan_info.vlan_oid); m_portList.erase(vlan.m_alias); m_port_ref_count.erase(vlan.m_alias); + m_vlanPorts.erase(vlan.m_alias); return true; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 4d069ccfc5..1a37ceebdd 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -2,6 +2,7 @@ #define SWSS_PORTSORCH_H #include +#include #include "acltable.h" #include "orch.h" @@ -150,6 +151,8 @@ class PortsOrch : public Orch, public Subject bool createVlanHostIntf(Port& vl, string hostif_name); bool removeVlanHostIntf(Port vl); + unordered_set& getAllVlans(); + bool createBindAclTableGroup(sai_object_id_t port_oid, sai_object_id_t acl_table_oid, sai_object_id_t &group_oid, @@ -306,6 +309,7 @@ class PortsOrch : public Orch, public Subject map m_gearboxPortMap; map> m_gearboxPortListLaneMap; + unordered_set m_vlanPorts; port_config_state_t m_portConfigState = PORT_CONFIG_MISSING; sai_uint32_t m_portCount; map, sai_object_id_t> m_portListLaneMap; diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index f2d45a19aa..ca3739244e 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -44,6 +44,7 @@ tests_SOURCES = aclorch_ut.cpp \ mock_table.cpp \ mock_hiredis.cpp \ mock_redisreply.cpp \ + mock_sai_api.cpp \ bulker_ut.cpp \ portmgr_ut.cpp \ sflowmgrd_ut.cpp \ @@ -55,6 +56,7 @@ tests_SOURCES = aclorch_ut.cpp \ mux_rollback_ut.cpp \ warmrestartassist_ut.cpp \ test_failure_handling.cpp \ + neighorch_ut.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ $(top_srcdir)/lib/subintf.cpp \ $(top_srcdir)/lib/recorder.cpp \ diff --git a/tests/mock_tests/flowcounterrouteorch_ut.cpp b/tests/mock_tests/flowcounterrouteorch_ut.cpp index cf80bda5bf..42c96c4c63 100644 --- a/tests/mock_tests/flowcounterrouteorch_ut.cpp +++ b/tests/mock_tests/flowcounterrouteorch_ut.cpp @@ -135,6 +135,7 @@ namespace flowcounterrouteorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, diff --git a/tests/mock_tests/mock_orch_test.h b/tests/mock_tests/mock_orch_test.h new file mode 100644 index 0000000000..eefda42057 --- /dev/null +++ b/tests/mock_tests/mock_orch_test.h @@ -0,0 +1,305 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "gtest/gtest.h" +#include + +using namespace std; +using ::testing::Return; +using ::testing::Throw; + +namespace mock_orch_test +{ + static const string PEER_SWITCH_HOSTNAME = "peer_hostname"; + static const string PEER_IPV4_ADDRESS = "1.1.1.1"; + static const string ACTIVE_INTERFACE = "Ethernet4"; + static const string STANDBY_INTERFACE = "Ethernet8"; + static const string ACTIVE_STATE = "active"; + static const string STANDBY_STATE = "standby"; + static const string STATE = "state"; + static const string VLAN_1000 = "Vlan1000"; + static const string VLAN_2000 = "Vlan2000"; + static const string SERVER_IP1 = "192.168.0.2"; + static const string SERVER_IP2 = "192.168.0.3"; + static const string MAC1 = "62:f9:65:10:2f:01"; + static const string MAC2 = "62:f9:65:10:2f:02"; + static const string MAC3 = "62:f9:65:10:2f:03"; + + class MockOrchTest: public ::testing::Test + { + protected: + std::vector ut_orch_list; + shared_ptr m_app_db; + shared_ptr m_config_db; + shared_ptr m_state_db; + shared_ptr m_chassis_app_db; + MuxOrch *m_MuxOrch; + MuxCableOrch *m_MuxCableOrch; + MuxCable *m_MuxCable; + TunnelDecapOrch *m_TunnelDecapOrch; + MuxStateOrch *m_MuxStateOrch; + FlexCounterOrch *m_FlexCounterOrch; + VxlanTunnelOrch *m_VxlanTunnelOrch; + + virtual void ApplyInitialConfigs() {} + + void PrepareSai() + { + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + sai_status_t status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + + /* Create a loopback underlay router interface */ + vector underlay_intf_attrs; + + sai_attribute_t underlay_intf_attr; + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; + underlay_intf_attr.value.oid = gVirtualRouterId; + underlay_intf_attrs.push_back(underlay_intf_attr); + + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; + underlay_intf_attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_LOOPBACK; + underlay_intf_attrs.push_back(underlay_intf_attr); + + underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; + underlay_intf_attr.value.u32 = 9100; + underlay_intf_attrs.push_back(underlay_intf_attr); + + status = sai_router_intfs_api->create_router_interface(&gUnderlayIfId, gSwitchId, (uint32_t)underlay_intf_attrs.size(), underlay_intf_attrs.data()); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + virtual void PostSetUp() {}; + + void SetUp() override + { + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); + + PrepareSai(); + + const int portsorch_base_pri = 40; + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + + m_FlexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(m_FlexCounterOrch); + ut_orch_list.push_back((Orch **)&m_FlexCounterOrch); + + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); + gDirectory.set(gFlowCounterRouteOrch); + ut_orch_list.push_back((Orch **)&gFlowCounterRouteOrch); + + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + gDirectory.set(gVrfOrch); + ut_orch_list.push_back((Orch **)&gVrfOrch); + + gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + gDirectory.set(gIntfsOrch); + ut_orch_list.push_back((Orch **)&gIntfsOrch); + + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); + ut_orch_list.push_back((Orch **)&gPortsOrch); + + const int fgnhgorch_pri = 15; + + vector fgnhg_tables = { + { CFG_FG_NHG, fgnhgorch_pri }, + { CFG_FG_NHG_PREFIX, fgnhgorch_pri }, + { CFG_FG_NHG_MEMBER, fgnhgorch_pri } + }; + + gFgNhgOrch = new FgNhgOrch(m_config_db.get(), m_app_db.get(), m_state_db.get(), fgnhg_tables, gNeighOrch, gIntfsOrch, gVrfOrch); + gDirectory.set(gFgNhgOrch); + ut_orch_list.push_back((Orch **)&gFgNhgOrch); + + const int fdborch_pri = 20; + + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri } + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); + gDirectory.set(gFdbOrch); + ut_orch_list.push_back((Orch **)&gFdbOrch); + + gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); + gDirectory.set(gNeighOrch); + ut_orch_list.push_back((Orch **)&gNeighOrch); + + m_TunnelDecapOrch = new TunnelDecapOrch(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); + gDirectory.set(m_TunnelDecapOrch); + ut_orch_list.push_back((Orch **)&m_TunnelDecapOrch); + vector mux_tables = { + CFG_MUX_CABLE_TABLE_NAME, + CFG_PEER_SWITCH_TABLE_NAME + }; + + vector buffer_tables = { + APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME + }; + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); + + TableConnector stateDbSwitchTable(m_state_db.get(), STATE_SWITCH_CAPABILITY_TABLE_NAME); + TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); + TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); + + vector switch_tables = { + conf_asic_sensors, + app_switch_table + }; + vector policer_tables = { + TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), + TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + + TableConnector stateDbStorm(m_state_db.get(), STATE_BUM_STORM_CAPABILITY_TABLE_NAME); + gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); + gDirectory.set(gPolicerOrch); + ut_orch_list.push_back((Orch **)&gPolicerOrch); + + gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); + gDirectory.set(gSwitchOrch); + ut_orch_list.push_back((Orch **)&gSwitchOrch); + + gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); + gDirectory.set(gNhgOrch); + ut_orch_list.push_back((Orch **)&gNhgOrch); + + vector srv6_tables = { + APP_SRV6_SID_LIST_TABLE_NAME, + APP_SRV6_MY_SID_TABLE_NAME + }; + gSrv6Orch = new Srv6Orch(m_app_db.get(), srv6_tables, gSwitchOrch, gVrfOrch, gNeighOrch); + gDirectory.set(gSrv6Orch); + ut_orch_list.push_back((Orch **)&gSrv6Orch); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + gDirectory.set(gCrmOrch); + ut_orch_list.push_back((Orch **)&gCrmOrch); + + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gDirectory.set(gRouteOrch); + ut_orch_list.push_back((Orch **)&gRouteOrch); + TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); + TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); + gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); + gDirectory.set(gMirrorOrch); + ut_orch_list.push_back((Orch **)&gMirrorOrch); + + TableConnector confDbAclTable(m_config_db.get(), CFG_ACL_TABLE_TABLE_NAME); + TableConnector confDbAclTableType(m_config_db.get(), CFG_ACL_TABLE_TYPE_TABLE_NAME); + TableConnector confDbAclRuleTable(m_config_db.get(), CFG_ACL_RULE_TABLE_NAME); + TableConnector appDbAclTable(m_app_db.get(), APP_ACL_TABLE_TABLE_NAME); + TableConnector appDbAclTableType(m_app_db.get(), APP_ACL_TABLE_TYPE_TABLE_NAME); + TableConnector appDbAclRuleTable(m_app_db.get(), APP_ACL_RULE_TABLE_NAME); + + vector acl_table_connectors = { + confDbAclTableType, + confDbAclTable, + confDbAclRuleTable, + appDbAclTable, + appDbAclRuleTable, + appDbAclTableType, + }; + gAclOrch = new AclOrch(acl_table_connectors, m_state_db.get(), + gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, NULL); + gDirectory.set(gAclOrch); + ut_orch_list.push_back((Orch **)&gAclOrch); + + m_MuxOrch = new MuxOrch(m_config_db.get(), mux_tables, m_TunnelDecapOrch, gNeighOrch, gFdbOrch); + gDirectory.set(m_MuxOrch); + ut_orch_list.push_back((Orch **)&m_MuxOrch); + + m_MuxCableOrch = new MuxCableOrch(m_app_db.get(), m_state_db.get(), APP_MUX_CABLE_TABLE_NAME); + gDirectory.set(m_MuxCableOrch); + ut_orch_list.push_back((Orch **)&m_MuxCableOrch); + + m_MuxStateOrch = new MuxStateOrch(m_state_db.get(), STATE_HW_MUX_CABLE_TABLE_NAME); + gDirectory.set(m_MuxStateOrch); + ut_orch_list.push_back((Orch **)&m_MuxStateOrch); + + m_VxlanTunnelOrch = new VxlanTunnelOrch(m_state_db.get(), m_app_db.get(), APP_VXLAN_TUNNEL_TABLE_NAME); + gDirectory.set(m_VxlanTunnelOrch); + ut_orch_list.push_back((Orch **)&m_VxlanTunnelOrch); + + ApplyInitialConfigs(); + PostSetUp(); + } + + virtual void PreTearDown() {}; + + void TearDown() override + { + PreTearDown(); + for (std::vector::reverse_iterator rit = ut_orch_list.rbegin(); rit != ut_orch_list.rend(); ++rit) + { + Orch **orch = *rit; + delete *orch; + *orch = nullptr; + } + + gDirectory.m_values.clear(); + + ut_helper::uninitSaiApi(); + } + }; +} \ No newline at end of file diff --git a/tests/mock_tests/mock_sai_api.cpp b/tests/mock_tests/mock_sai_api.cpp new file mode 100644 index 0000000000..1f7e7e63ef --- /dev/null +++ b/tests/mock_tests/mock_sai_api.cpp @@ -0,0 +1,25 @@ +#include "mock_sai_api.h" + +std::set apply_mock_fns; +std::set remove_mock_fns; + +void MockSaiApis() +{ + if (apply_mock_fns.empty()) + { + EXPECT_TRUE(false) << "No mock application functions found. Did you call DEFINE_SAI_API_MOCK and INIT_SAI_API_MOCK for the necessary SAI object type?"; + } + + for (auto apply_fn : apply_mock_fns) + { + (*apply_fn)(); + } +} + +void RestoreSaiApis() +{ + for (auto remove_fn : remove_mock_fns) + { + (*remove_fn)(); + } +} \ No newline at end of file diff --git a/tests/mock_tests/mock_sai_api.h b/tests/mock_tests/mock_sai_api.h index 63d8921bf1..7819b5b126 100644 --- a/tests/mock_tests/mock_sai_api.h +++ b/tests/mock_tests/mock_sai_api.h @@ -1,11 +1,26 @@ +#ifndef MOCK_SAI_API_H +#define MOCK_SAI_API_H #include "mock_orchagent_main.h" #include +/* +To mock a particular SAI API: +1. At the top of the test CPP file using the mock, call DEFINE_SAI_API_MOCK or DEFINE_SAI_GENERIC_API_MOCK + for each SAI API you want to mock. +2. At the top of the test CPP file using the mock, call EXTERN_MOCK_FNS. +3. In the SetUp method of the test class, call INIT_SAI_API_MOCK for each SAI API you want to mock. +4. In the SetUp method of the test class, call MockSaiApis. +5. In the TearDown method of the test class, call RestoreSaiApis. +*/ + using ::testing::Return; using ::testing::NiceMock; -std::set apply_mock_fns; -std::set remove_mock_fns; +#define EXTERN_MOCK_FNS \ + extern std::set apply_mock_fns; \ + extern std::set remove_mock_fns; + +EXTERN_MOCK_FNS #define CREATE_PARAMS(sai_object_type) _In_ const sai_##sai_object_type##_entry_t *sai_object_type##_entry, _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list #define REMOVE_PARAMS(sai_object_type) _In_ const sai_##sai_object_type##_entry_t *sai_object_type##_entry @@ -27,8 +42,8 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the 7. Define a method to remove the mock */ #define DEFINE_SAI_API_MOCK(sai_object_type) \ - sai_##sai_object_type##_api_t *old_sai_##sai_object_type##_api; \ - sai_##sai_object_type##_api_t ut_sai_##sai_object_type##_api; \ + static sai_##sai_object_type##_api_t *old_sai_##sai_object_type##_api; \ + static sai_##sai_object_type##_api_t ut_sai_##sai_object_type##_api; \ class mock_sai_##sai_object_type##_api_t \ { \ public: \ @@ -48,16 +63,16 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the MOCK_METHOD3(create_##sai_object_type##_entry, sai_status_t(CREATE_PARAMS(sai_object_type))); \ MOCK_METHOD1(remove_##sai_object_type##_entry, sai_status_t(REMOVE_PARAMS(sai_object_type))); \ }; \ - mock_sai_##sai_object_type##_api_t *mock_sai_##sai_object_type##_api; \ - sai_status_t mock_create_##sai_object_type##_entry(CREATE_PARAMS(sai_object_type)) \ + static mock_sai_##sai_object_type##_api_t *mock_sai_##sai_object_type##_api; \ + inline sai_status_t mock_create_##sai_object_type##_entry(CREATE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_object_type##_api->create_##sai_object_type##_entry(CREATE_ARGS(sai_object_type)); \ } \ - sai_status_t mock_remove_##sai_object_type##_entry(REMOVE_PARAMS(sai_object_type)) \ + inline sai_status_t mock_remove_##sai_object_type##_entry(REMOVE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_object_type##_api->remove_##sai_object_type##_entry(REMOVE_ARGS(sai_object_type)); \ } \ - void apply_sai_##sai_object_type##_api_mock() \ + inline void apply_sai_##sai_object_type##_api_mock() \ { \ mock_sai_##sai_object_type##_api = new NiceMock(); \ \ @@ -68,15 +83,15 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the sai_##sai_object_type##_api->create_##sai_object_type##_entry = mock_create_##sai_object_type##_entry; \ sai_##sai_object_type##_api->remove_##sai_object_type##_entry = mock_remove_##sai_object_type##_entry; \ } \ - void remove_sai_##sai_object_type##_api_mock() \ + inline void remove_sai_##sai_object_type##_api_mock() \ { \ sai_##sai_object_type##_api = old_sai_##sai_object_type##_api; \ delete mock_sai_##sai_object_type##_api; \ } #define DEFINE_SAI_GENERIC_API_MOCK(sai_api_name, sai_object_type) \ - sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ - sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t *old_sai_##sai_api_name##_api; \ + static sai_##sai_api_name##_api_t ut_sai_##sai_api_name##_api; \ class mock_sai_##sai_api_name##_api_t \ { \ public: \ @@ -96,16 +111,16 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the MOCK_METHOD4(create_##sai_object_type, sai_status_t(GENERIC_CREATE_PARAMS(sai_object_type))); \ MOCK_METHOD1(remove_##sai_object_type, sai_status_t(GENERIC_REMOVE_PARAMS(sai_object_type))); \ }; \ - mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ - sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) \ + static mock_sai_##sai_api_name##_api_t *mock_sai_##sai_api_name##_api; \ + inline sai_status_t mock_create_##sai_object_type(GENERIC_CREATE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_api_name##_api->create_##sai_object_type(GENERIC_CREATE_ARGS(sai_object_type)); \ } \ - sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) \ + inline sai_status_t mock_remove_##sai_object_type(GENERIC_REMOVE_PARAMS(sai_object_type)) \ { \ return mock_sai_##sai_api_name##_api->remove_##sai_object_type(GENERIC_REMOVE_ARGS(sai_object_type)); \ } \ - void apply_sai_##sai_api_name##_api_mock() \ + inline void apply_sai_##sai_api_name##_api_mock() \ { \ mock_sai_##sai_api_name##_api = new NiceMock(); \ \ @@ -116,7 +131,7 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the sai_##sai_api_name##_api->create_##sai_object_type = mock_create_##sai_object_type; \ sai_##sai_api_name##_api->remove_##sai_object_type = mock_remove_##sai_object_type; \ } \ - void remove_sai_##sai_api_name##_api_mock() \ + inline void remove_sai_##sai_api_name##_api_mock() \ { \ sai_##sai_api_name##_api = old_sai_##sai_api_name##_api; \ delete mock_sai_##sai_api_name##_api; \ @@ -127,23 +142,6 @@ The macro DEFINE_SAI_API_MOCK will perform the steps to mock the SAI API for the apply_mock_fns.insert(&apply_sai_##sai_object_type##_api_mock); \ remove_mock_fns.insert(&remove_sai_##sai_object_type##_api_mock); -void MockSaiApis() -{ - if (apply_mock_fns.empty()) - { - EXPECT_TRUE(false) << "No mock application functions found. Did you call DEFINE_SAI_API_MOCK and INIT_SAI_API_MOCK for the necessary SAI object type?"; - } - - for (auto apply_fn : apply_mock_fns) - { - (*apply_fn)(); - } -} - -void RestoreSaiApis() -{ - for (auto remove_fn : remove_mock_fns) - { - (*remove_fn)(); - } -} +void MockSaiApis(); +void RestoreSaiApis(); +#endif \ No newline at end of file diff --git a/tests/mock_tests/mux_rollback_ut.cpp b/tests/mock_tests/mux_rollback_ut.cpp index 578b6c817b..933c27aca2 100644 --- a/tests/mock_tests/mux_rollback_ut.cpp +++ b/tests/mock_tests/mux_rollback_ut.cpp @@ -7,45 +7,28 @@ #include "ut_helper.h" #include "mock_orchagent_main.h" #include "mock_sai_api.h" +#include "mock_orch_test.h" #include "gtest/gtest.h" #include -DEFINE_SAI_API_MOCK(neighbor); -DEFINE_SAI_API_MOCK(route); -DEFINE_SAI_GENERIC_API_MOCK(acl, acl_entry); -DEFINE_SAI_GENERIC_API_MOCK(next_hop, next_hop); +EXTERN_MOCK_FNS namespace mux_rollback_test { + DEFINE_SAI_API_MOCK(neighbor); + DEFINE_SAI_API_MOCK(route); + DEFINE_SAI_GENERIC_API_MOCK(acl, acl_entry); + DEFINE_SAI_GENERIC_API_MOCK(next_hop, next_hop); using namespace std; + using namespace mock_orch_test; using ::testing::Return; using ::testing::Throw; - static const string PEER_SWITCH_HOSTNAME = "peer_hostname"; - static const string PEER_IPV4_ADDRESS = "1.1.1.1"; static const string TEST_INTERFACE = "Ethernet4"; - static const string ACTIVE = "active"; - static const string STANDBY = "standby"; - static const string STATE = "state"; - static const string VLAN_NAME = "Vlan1000"; - static const string SERVER_IP = "192.168.0.2"; - class MuxRollbackTest : public ::testing::Test + class MuxRollbackTest : public MockOrchTest { protected: - std::vector ut_orch_list; - shared_ptr m_app_db; - shared_ptr m_config_db; - shared_ptr m_state_db; - shared_ptr m_chassis_app_db; - MuxOrch *m_MuxOrch; - MuxCableOrch *m_MuxCableOrch; - MuxCable *m_MuxCable; - TunnelDecapOrch *m_TunnelDecapOrch; - MuxStateOrch *m_MuxStateOrch; - FlexCounterOrch *m_FlexCounterOrch; - mock_sai_neighbor_api_t mock_sai_neighbor_api_; - void SetMuxStateFromAppDb(std::string state) { Table mux_cable_table = Table(m_app_db.get(), APP_MUX_CABLE_TABLE_NAME); @@ -60,7 +43,7 @@ namespace mux_rollback_test EXPECT_EQ(state, m_MuxCable->getState()); } - void ApplyDualTorConfigs() + void ApplyInitialConfigs() { Table peer_switch_table = Table(m_config_db.get(), CFG_PEER_SWITCH_TABLE_NAME); Table tunnel_table = Table(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); @@ -77,21 +60,21 @@ namespace mux_rollback_test port_table.set("PortInitDone", { {} }); neigh_table.set( - VLAN_NAME + neigh_table.getTableNameSeparator() + SERVER_IP, { { "neigh", "62:f9:65:10:2f:04" }, + VLAN_1000 + neigh_table.getTableNameSeparator() + SERVER_IP1, { { "neigh", "62:f9:65:10:2f:04" }, { "family", "IPv4" } }); - vlan_table.set(VLAN_NAME, { { "admin_status", "up" }, + vlan_table.set(VLAN_1000, { { "admin_status", "up" }, { "mtu", "9100" }, { "mac", "00:aa:bb:cc:dd:ee" } }); vlan_member_table.set( - VLAN_NAME + vlan_member_table.getTableNameSeparator() + TEST_INTERFACE, + VLAN_1000 + vlan_member_table.getTableNameSeparator() + TEST_INTERFACE, { { "tagging_mode", "untagged" } }); - intf_table.set(VLAN_NAME, { { "grat_arp", "enabled" }, + intf_table.set(VLAN_1000, { { "grat_arp", "enabled" }, { "proxy_arp", "enabled" }, { "mac_addr", "00:00:00:00:00:00" } }); intf_table.set( - VLAN_NAME + neigh_table.getTableNameSeparator() + "192.168.0.1/21", { + VLAN_1000 + neigh_table.getTableNameSeparator() + "192.168.0.1/21", { { "scope", "global" }, { "family", "IPv4" }, }); @@ -105,7 +88,7 @@ namespace mux_rollback_test peer_switch_table.set(PEER_SWITCH_HOSTNAME, { { "address_ipv4", PEER_IPV4_ADDRESS } }); - mux_cable_table.set(TEST_INTERFACE, { { "server_ipv4", SERVER_IP + "/32" }, + mux_cable_table.set(TEST_INTERFACE, { { "server_ipv4", SERVER_IP1 + "/32" }, { "server_ipv6", "a::a/128" }, { "state", "auto" } }); @@ -132,237 +115,11 @@ namespace mux_rollback_test m_MuxCable = m_MuxOrch->getMuxCable(TEST_INTERFACE); // We always expect the mux to be initialized to standby - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } - void PrepareSai() + void PostSetUp() override { - sai_attribute_t attr; - - attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; - attr.value.booldata = true; - - sai_status_t status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - // Get switch source MAC address - attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - gMacAddress = attr.value.mac; - - attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; - status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - - gVirtualRouterId = attr.value.oid; - - /* Create a loopback underlay router interface */ - vector underlay_intf_attrs; - - sai_attribute_t underlay_intf_attr; - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID; - underlay_intf_attr.value.oid = gVirtualRouterId; - underlay_intf_attrs.push_back(underlay_intf_attr); - - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE; - underlay_intf_attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_LOOPBACK; - underlay_intf_attrs.push_back(underlay_intf_attr); - - underlay_intf_attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; - underlay_intf_attr.value.u32 = 9100; - underlay_intf_attrs.push_back(underlay_intf_attr); - - status = sai_router_intfs_api->create_router_interface(&gUnderlayIfId, gSwitchId, (uint32_t)underlay_intf_attrs.size(), underlay_intf_attrs.data()); - ASSERT_EQ(status, SAI_STATUS_SUCCESS); - } - - void SetUp() override - { - map profile = { - { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, - { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } - }; - - ut_helper::initSaiApi(profile); - m_app_db = make_shared("APPL_DB", 0); - m_config_db = make_shared("CONFIG_DB", 0); - m_state_db = make_shared("STATE_DB", 0); - m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); - - PrepareSai(); - - const int portsorch_base_pri = 40; - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - vector flex_counter_tables = { - CFG_FLEX_COUNTER_TABLE_NAME - }; - - m_FlexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); - gDirectory.set(m_FlexCounterOrch); - ut_orch_list.push_back((Orch **)&m_FlexCounterOrch); - - static const vector route_pattern_tables = { - CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, - }; - gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); - gDirectory.set(gFlowCounterRouteOrch); - ut_orch_list.push_back((Orch **)&gFlowCounterRouteOrch); - - gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); - gDirectory.set(gVrfOrch); - ut_orch_list.push_back((Orch **)&gVrfOrch); - - gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); - gDirectory.set(gIntfsOrch); - ut_orch_list.push_back((Orch **)&gIntfsOrch); - - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - gDirectory.set(gPortsOrch); - ut_orch_list.push_back((Orch **)&gPortsOrch); - - const int fgnhgorch_pri = 15; - - vector fgnhg_tables = { - { CFG_FG_NHG, fgnhgorch_pri }, - { CFG_FG_NHG_PREFIX, fgnhgorch_pri }, - { CFG_FG_NHG_MEMBER, fgnhgorch_pri } - }; - - gFgNhgOrch = new FgNhgOrch(m_config_db.get(), m_app_db.get(), m_state_db.get(), fgnhg_tables, gNeighOrch, gIntfsOrch, gVrfOrch); - gDirectory.set(gFgNhgOrch); - ut_orch_list.push_back((Orch **)&gFgNhgOrch); - - const int fdborch_pri = 20; - - vector app_fdb_tables = { - { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri }, - { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri } - }; - - TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); - TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); - gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); - gDirectory.set(gFdbOrch); - ut_orch_list.push_back((Orch **)&gFdbOrch); - - gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); - gDirectory.set(gNeighOrch); - ut_orch_list.push_back((Orch **)&gNeighOrch); - - m_TunnelDecapOrch = new TunnelDecapOrch(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); - gDirectory.set(m_TunnelDecapOrch); - ut_orch_list.push_back((Orch **)&m_TunnelDecapOrch); - vector mux_tables = { - CFG_MUX_CABLE_TABLE_NAME, - CFG_PEER_SWITCH_TABLE_NAME - }; - - vector buffer_tables = { - APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME - }; - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - - TableConnector stateDbSwitchTable(m_state_db.get(), STATE_SWITCH_CAPABILITY_TABLE_NAME); - TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); - TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); - - vector switch_tables = { - conf_asic_sensors, - app_switch_table - }; - vector policer_tables = { - TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), - TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) - }; - - TableConnector stateDbStorm(m_state_db.get(), STATE_BUM_STORM_CAPABILITY_TABLE_NAME); - gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); - gDirectory.set(gPolicerOrch); - ut_orch_list.push_back((Orch **)&gPolicerOrch); - - gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); - gDirectory.set(gSwitchOrch); - ut_orch_list.push_back((Orch **)&gSwitchOrch); - - gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); - gDirectory.set(gNhgOrch); - ut_orch_list.push_back((Orch **)&gNhgOrch); - - vector srv6_tables = { - APP_SRV6_SID_LIST_TABLE_NAME, - APP_SRV6_MY_SID_TABLE_NAME - }; - gSrv6Orch = new Srv6Orch(m_app_db.get(), srv6_tables, gSwitchOrch, gVrfOrch, gNeighOrch); - gDirectory.set(gSrv6Orch); - ut_orch_list.push_back((Orch **)&gSrv6Orch); - gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); - gDirectory.set(gCrmOrch); - ut_orch_list.push_back((Orch **)&gCrmOrch); - - const int routeorch_pri = 5; - vector route_tables = { - { APP_ROUTE_TABLE_NAME, routeorch_pri }, - { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } - }; - gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); - gDirectory.set(gRouteOrch); - ut_orch_list.push_back((Orch **)&gRouteOrch); - TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); - TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); - gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); - gDirectory.set(gMirrorOrch); - ut_orch_list.push_back((Orch **)&gMirrorOrch); - - TableConnector confDbAclTable(m_config_db.get(), CFG_ACL_TABLE_TABLE_NAME); - TableConnector confDbAclTableType(m_config_db.get(), CFG_ACL_TABLE_TYPE_TABLE_NAME); - TableConnector confDbAclRuleTable(m_config_db.get(), CFG_ACL_RULE_TABLE_NAME); - TableConnector appDbAclTable(m_app_db.get(), APP_ACL_TABLE_TABLE_NAME); - TableConnector appDbAclTableType(m_app_db.get(), APP_ACL_TABLE_TYPE_TABLE_NAME); - TableConnector appDbAclRuleTable(m_app_db.get(), APP_ACL_RULE_TABLE_NAME); - - vector acl_table_connectors = { - confDbAclTableType, - confDbAclTable, - confDbAclRuleTable, - appDbAclTable, - appDbAclRuleTable, - appDbAclTableType, - }; - gAclOrch = new AclOrch(acl_table_connectors, m_state_db.get(), - gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, NULL); - gDirectory.set(gAclOrch); - ut_orch_list.push_back((Orch **)&gAclOrch); - - m_MuxOrch = new MuxOrch(m_config_db.get(), mux_tables, m_TunnelDecapOrch, gNeighOrch, gFdbOrch); - gDirectory.set(m_MuxOrch); - ut_orch_list.push_back((Orch **)&m_MuxOrch); - - m_MuxCableOrch = new MuxCableOrch(m_app_db.get(), m_state_db.get(), APP_MUX_CABLE_TABLE_NAME); - gDirectory.set(m_MuxCableOrch); - ut_orch_list.push_back((Orch **)&m_MuxCableOrch); - - m_MuxStateOrch = new MuxStateOrch(m_state_db.get(), STATE_HW_MUX_CABLE_TABLE_NAME); - gDirectory.set(m_MuxStateOrch); - ut_orch_list.push_back((Orch **)&m_MuxStateOrch); - - ApplyDualTorConfigs(); INIT_SAI_API_MOCK(neighbor); INIT_SAI_API_MOCK(route); INIT_SAI_API_MOCK(acl); @@ -370,19 +127,9 @@ namespace mux_rollback_test MockSaiApis(); } - void TearDown() override + void PreTearDown() override { - for (std::vector::reverse_iterator rit = ut_orch_list.rbegin(); rit != ut_orch_list.rend(); ++rit) - { - Orch **orch = *rit; - delete *orch; - *orch = nullptr; - } - - gDirectory.m_values.clear(); - RestoreSaiApis(); - ut_helper::uninitSaiApi(); } }; @@ -390,110 +137,110 @@ namespace mux_rollback_test { EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyNeighborNotFound) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveRouteNotFound) { EXPECT_CALL(*mock_sai_route_api, remove_route_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyRouteAlreadyExists) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_route_api, create_route_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveAclNotFound) { EXPECT_CALL(*mock_sai_acl_api, remove_acl_entry) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyAclAlreadyExists) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_acl_api, create_acl_entry) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveNextHopAlreadyExists) { EXPECT_CALL(*mock_sai_next_hop_api, create_next_hop) .WillOnce(Return(SAI_STATUS_ITEM_ALREADY_EXISTS)); - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); } TEST_F(MuxRollbackTest, ActiveToStandbyNextHopNotFound) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_next_hop_api, remove_next_hop) .WillOnce(Return(SAI_STATUS_ITEM_NOT_FOUND)); - SetAndAssertMuxState(STANDBY); + SetAndAssertMuxState(STANDBY_STATE); } TEST_F(MuxRollbackTest, StandbyToActiveRuntimeErrorRollbackToStandby) { EXPECT_CALL(*mock_sai_route_api, remove_route_entry) .WillOnce(Throw(runtime_error("Mock runtime error"))); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyRuntimeErrorRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_route_api, create_route_entry) .WillOnce(Throw(runtime_error("Mock runtime error"))); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, StandbyToActiveLogicErrorRollbackToStandby) { EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry) .WillOnce(Throw(logic_error("Mock logic error"))); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyLogicErrorRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry) .WillOnce(Throw(logic_error("Mock logic error"))); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, StandbyToActiveExceptionRollbackToStandby) { EXPECT_CALL(*mock_sai_next_hop_api, create_next_hop) .WillOnce(Throw(exception())); - SetMuxStateFromAppDb(ACTIVE); - EXPECT_EQ(STANDBY, m_MuxCable->getState()); + SetMuxStateFromAppDb(ACTIVE_STATE); + EXPECT_EQ(STANDBY_STATE, m_MuxCable->getState()); } TEST_F(MuxRollbackTest, ActiveToStandbyExceptionRollbackToActive) { - SetAndAssertMuxState(ACTIVE); + SetAndAssertMuxState(ACTIVE_STATE); EXPECT_CALL(*mock_sai_next_hop_api, remove_next_hop) .WillOnce(Throw(exception())); - SetMuxStateFromAppDb(STANDBY); - EXPECT_EQ(ACTIVE, m_MuxCable->getState()); + SetMuxStateFromAppDb(STANDBY_STATE); + EXPECT_EQ(ACTIVE_STATE, m_MuxCable->getState()); } } diff --git a/tests/mock_tests/neighorch_ut.cpp b/tests/mock_tests/neighorch_ut.cpp new file mode 100644 index 0000000000..03957436a6 --- /dev/null +++ b/tests/mock_tests/neighorch_ut.cpp @@ -0,0 +1,198 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_sai_api.h" +#include "mock_orch_test.h" + + +EXTERN_MOCK_FNS + +namespace neighorch_test +{ + DEFINE_SAI_API_MOCK(neighbor); + using namespace std; + using namespace mock_orch_test; + using ::testing::Return; + using ::testing::Throw; + + static const string TEST_IP = "10.10.10.10"; + static const NeighborEntry VLAN1000_NEIGH = NeighborEntry(TEST_IP, VLAN_1000); + static const NeighborEntry VLAN2000_NEIGH = NeighborEntry(TEST_IP, VLAN_2000); + + class NeighOrchTest: public MockOrchTest + { + protected: + void SetAndAssertMuxState(std::string interface, std::string state) + { + MuxCable* muxCable = m_MuxOrch->getMuxCable(interface); + muxCable->setState(state); + EXPECT_EQ(state, muxCable->getState()); + } + + void LearnNeighbor(std::string vlan, std::string ip, std::string mac) + { + Table neigh_table = Table(m_app_db.get(), APP_NEIGH_TABLE_NAME); + string key = vlan + neigh_table.getTableNameSeparator() + ip; + neigh_table.set(key, { { "neigh", mac }, { "family", "IPv4" } }); + gNeighOrch->addExistingData(&neigh_table); + static_cast(gNeighOrch)->doTask(); + neigh_table.del(key); + } + + void ApplyInitialConfigs() + { + Table peer_switch_table = Table(m_config_db.get(), CFG_PEER_SWITCH_TABLE_NAME); + Table tunnel_table = Table(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); + Table mux_cable_table = Table(m_config_db.get(), CFG_MUX_CABLE_TABLE_NAME); + Table port_table = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table vlan_table = Table(m_app_db.get(), APP_VLAN_TABLE_NAME); + Table vlan_member_table = Table(m_app_db.get(), APP_VLAN_MEMBER_TABLE_NAME); + Table neigh_table = Table(m_app_db.get(), APP_NEIGH_TABLE_NAME); + Table intf_table = Table(m_app_db.get(), APP_INTF_TABLE_NAME); + Table fdb_table = Table(m_app_db.get(), APP_FDB_TABLE_NAME); + + auto ports = ut_helper::getInitialSaiPorts(); + port_table.set(ACTIVE_INTERFACE, ports[ACTIVE_INTERFACE]); + port_table.set(STANDBY_INTERFACE, ports[STANDBY_INTERFACE]); + port_table.set("PortConfigDone", { { "count", to_string(1) } }); + port_table.set("PortInitDone", { {} }); + + vlan_table.set(VLAN_1000, { { "admin_status", "up" }, + { "mtu", "9100" }, + { "mac", "00:aa:bb:cc:dd:ee" } }); + vlan_table.set(VLAN_2000, { { "admin_status", "up"}, + { "mtu", "9100" }, + { "mac", "aa:11:bb:22:cc:33" } }); + vlan_member_table.set( + VLAN_1000 + vlan_member_table.getTableNameSeparator() + ACTIVE_INTERFACE, + { { "tagging_mode", "untagged" } }); + + vlan_member_table.set( + VLAN_2000 + vlan_member_table.getTableNameSeparator() + STANDBY_INTERFACE, + { { "tagging_mode", "untagged" } }); + + intf_table.set(VLAN_1000, { { "grat_arp", "enabled" }, + { "proxy_arp", "enabled" }, + { "mac_addr", "00:00:00:00:00:00" } }); + + intf_table.set(VLAN_2000, { { "grat_arp", "enabled" }, + { "proxy_arp", "enabled" }, + { "mac_addr", "00:00:00:00:00:00" } }); + + intf_table.set( + VLAN_1000 + neigh_table.getTableNameSeparator() + "192.168.0.1/24", { + { "scope", "global" }, + { "family", "IPv4" }, + }); + + intf_table.set( + VLAN_2000 + neigh_table.getTableNameSeparator() + "192.168.2.1/24", { + { "scope", "global" }, + { "family", "IPv4" }, + }); + tunnel_table.set(MUX_TUNNEL, { { "dscp_mode", "uniform" }, + { "dst_ip", "2.2.2.2" }, + { "ecn_mode", "copy_from_outer" }, + { "encap_ecn_mode", "standard" }, + { "ttl_mode", "pipe" }, + { "tunnel_type", "IPINIP" } }); + + peer_switch_table.set(PEER_SWITCH_HOSTNAME, { { "address_ipv4", PEER_IPV4_ADDRESS } }); + + mux_cable_table.set(ACTIVE_INTERFACE, { { "server_ipv4", SERVER_IP1 + "/32" }, + { "server_ipv6", "a::a/128" }, + { "state", "auto" } }); + + mux_cable_table.set(STANDBY_INTERFACE, { { "server_ipv4", SERVER_IP2+ "/32" }, + { "server_ipv6", "a::b/128" }, + { "state", "auto" } }); + + gPortsOrch->addExistingData(&port_table); + gPortsOrch->addExistingData(&vlan_table); + gPortsOrch->addExistingData(&vlan_member_table); + static_cast(gPortsOrch)->doTask(); + + gIntfsOrch->addExistingData(&intf_table); + static_cast(gIntfsOrch)->doTask(); + + m_TunnelDecapOrch->addExistingData(&tunnel_table); + static_cast(m_TunnelDecapOrch)->doTask(); + + m_MuxOrch->addExistingData(&peer_switch_table); + static_cast(m_MuxOrch)->doTask(); + + m_MuxOrch->addExistingData(&mux_cable_table); + static_cast(m_MuxOrch)->doTask(); + + fdb_table.set( + VLAN_1000 + fdb_table.getTableNameSeparator() + MAC1, + { { "type", "dynamic" }, + { "port", ACTIVE_INTERFACE } }); + + fdb_table.set( + VLAN_2000 + fdb_table.getTableNameSeparator() + MAC2, + { { "type", "dynamic" }, + { "port", STANDBY_INTERFACE} }); + + fdb_table.set( + VLAN_1000 + fdb_table.getTableNameSeparator() + MAC3, + { { "type", "dynamic" }, + { "port", ACTIVE_INTERFACE} }); + + gFdbOrch->addExistingData(&fdb_table); + static_cast(gFdbOrch)->doTask(); + + SetAndAssertMuxState(ACTIVE_INTERFACE, ACTIVE_STATE); + SetAndAssertMuxState(STANDBY_INTERFACE, STANDBY_STATE); + } + + void PostSetUp() override + { + INIT_SAI_API_MOCK(neighbor); + MockSaiApis(); + } + + void PreTearDown() override + { + RestoreSaiApis(); + } + }; + + TEST_F(NeighOrchTest, MultiVlanIpLearning) + { + + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + + EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry); + LearnNeighbor(VLAN_2000, TEST_IP, MAC2); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 0); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 1); + + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC3); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 0); + } + + TEST_F(NeighOrchTest, MultiVlanUnableToRemoveNeighbor) + { + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry); + LearnNeighbor(VLAN_1000, TEST_IP, MAC1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + NextHopKey nexthop = { TEST_IP, VLAN_1000 }; + gNeighOrch->m_syncdNextHops[nexthop].ref_count = 1; + + EXPECT_CALL(*mock_sai_neighbor_api, remove_neighbor_entry).Times(0); + EXPECT_CALL(*mock_sai_neighbor_api, create_neighbor_entry).Times(0); + LearnNeighbor(VLAN_2000, TEST_IP, MAC2); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN1000_NEIGH), 1); + ASSERT_EQ(gNeighOrch->m_syncdNeighbors.count(VLAN2000_NEIGH), 0); + } +} diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index a96819e56e..75829d2122 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -178,6 +178,7 @@ namespace routeorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + gDirectory.set(gPortsOrch); vector flex_counter_tables = { CFG_FLEX_COUNTER_TABLE_NAME