From 4a242e2f59278ef2598430de6a3891210bedb8d9 Mon Sep 17 00:00:00 2001 From: Xichen Lin Date: Sun, 19 Jul 2026 21:26:35 +1000 Subject: [PATCH 1/2] [202605][intfsorch]: Fence route and neighbor creation during interface removal Keep the existing whole-interface DEL retry fenced after prefix deletion. Standard route, neighbor, and neighbor-next-hop creation now retries instead of attaching to the RIF being removed, while teardown continues to use the raw RIF lookup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6b568a1-5d2a-4309-b1ac-ef21ca155079 Signed-off-by: Xichen Lin --- orchagent/intfsorch.cpp | 21 ++++++++++++++++++--- orchagent/intfsorch.h | 1 + orchagent/neighorch.cpp | 9 +++++++-- orchagent/routeorch.cpp | 12 +++++++++--- tests/mock_tests/intfsorch_ut.cpp | 15 ++++++++++++++- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 22956c00b65..256a72ed7d5 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -116,6 +116,16 @@ sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias) return port.m_rif_id; } +sai_object_id_t IntfsOrch::getRouterIntfsIdForNewDependency(const string &alias) +{ + if (m_removingIntfses.find(alias) != m_removingIntfses.end()) + { + return SAI_NULL_OBJECT_ID; + } + + return getRouterIntfsId(alias); +} + bool IntfsOrch::isPrefixSubnet(const IpPrefix &ip_prefix, const string &alias) { if (m_syncdIntfses.find(alias) == m_syncdIntfses.end()) @@ -1129,12 +1139,18 @@ void IntfsOrch::doTask(Consumer &consumer) { if (removeIntf(alias, port.m_vr_id, ip_prefix_in_key ? &ip_prefix : nullptr)) { - m_removingIntfses.erase(alias); + if (!ip_prefix_in_key) + { + m_removingIntfses.erase(alias); + } it = consumer.m_toSync.erase(it); } else { - m_removingIntfses.insert(alias); + if (!ip_prefix_in_key) + { + m_removingIntfses.insert(alias); + } it++; continue; } @@ -1779,4 +1795,3 @@ void IntfsOrch::voqSyncIntfState(string &alias, bool isUp) } } - diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index aa5129bef45..fa1b9088cc6 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -36,6 +36,7 @@ class IntfsOrch : public Orch IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch, DBConnector *chassisAppDb); sai_object_id_t getRouterIntfsId(const string&); + sai_object_id_t getRouterIntfsIdForNewDependency(const string&); bool isPrefixSubnet(const IpPrefix&, const string&); bool isInbandIntfInMgmtVrf(const string& alias); string getRouterIntfsAlias(const IpAddress &ip, const string &vrf_name = ""); diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 9c7899ef953..e5364680000 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -268,7 +268,12 @@ bool NeighOrch::addNextHop(NeighborContext& ctx) } assert(!hasNextHop(nexthop)); - sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(nh.alias); + sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsIdForNewDependency(nh.alias); + if (rif_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to get rif_id for %s", nh.alias.c_str()); + return false; + } vector next_hop_attrs; @@ -1201,7 +1206,7 @@ bool NeighOrch::addNeighbor(NeighborContext& ctx) string alias = neighborEntry.alias; bool bulk_op = ctx.bulk_op; - sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(alias); + sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsIdForNewDependency(alias); if (rif_id == SAI_NULL_OBJECT_ID) { SWSS_LOG_INFO("Failed to get rif_id for %s", alias.c_str()); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 54448b88202..dc217d8c492 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -1515,7 +1515,10 @@ bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) m_neighOrch->hasNextHop(NextHopKey(it.ip_address, it.alias))) { NeighborContext ctx = NeighborContext(it); - m_neighOrch->addNextHop(ctx); + if (!m_neighOrch->addNextHop(ctx)) + { + return false; + } next_hop_id = m_neighOrch->getNextHopId(it); } else @@ -2091,7 +2094,7 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) return true; } - next_hop_id = m_intfsOrch->getRouterIntfsId(nexthop.alias); + next_hop_id = m_intfsOrch->getRouterIntfsIdForNewDependency(nexthop.alias); /* rif is not created yet */ if (next_hop_id == SAI_NULL_OBJECT_ID) { @@ -2126,7 +2129,10 @@ bool RouteOrch::addRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextHops) { /* since IP neighbor NH exists, neighbor is resolved, add MPLS NH */ NeighborContext ctx = NeighborContext(nexthop); - m_neighOrch->addNextHop(ctx); + if (!m_neighOrch->addNextHop(ctx)) + { + return false; + } next_hop_id = m_neighOrch->getNextHopId(nexthop); } /* IP neighbor is not yet resolved */ diff --git a/tests/mock_tests/intfsorch_ut.cpp b/tests/mock_tests/intfsorch_ut.cpp index e3a7d2eccd5..9e1f65100ae 100644 --- a/tests/mock_tests/intfsorch_ut.cpp +++ b/tests/mock_tests/intfsorch_ut.cpp @@ -308,17 +308,30 @@ namespace intfsorch_test static_cast(gIntfsOrch)->doTask(); ASSERT_EQ(current_create_count + 1, create_rif_count); + // Add a prefix so the whole-interface DEL is processed before its dependency DEL. + entries.clear(); + entries.push_back({"Ethernet0:10.0.0.1/24", "SET", { + {"scope", "global"}, + {"family", "IPv4"} + }}); + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + // create dependency to the interface gIntfsOrch->increaseRouterIntfsRefCount("Ethernet0"); - // delete the interface, expect retry because dependency exists + // Delete the interface and prefix in lexical order. The successful prefix + // DEL must not clear the whole-interface removal fence. entries.clear(); entries.push_back({"Ethernet0", "DEL", { {} }}); + entries.push_back({"Ethernet0:10.0.0.1/24", "DEL", { {} }}); consumer = dynamic_cast(gIntfsOrch->getExecutor(APP_INTF_TABLE_NAME)); consumer->addToSync(entries); auto current_remove_count = remove_rif_count; static_cast(gIntfsOrch)->doTask(); ASSERT_EQ(current_remove_count, remove_rif_count); + ASSERT_EQ(consumer->m_toSync.size(), 1u); + ASSERT_EQ(gIntfsOrch->getRouterIntfsIdForNewDependency("Ethernet0"), SAI_NULL_OBJECT_ID); // create the interface again, expect retry because interface is in removing entries.clear(); From 12a8297c2d56335723f5a87f55b369c58f424d7a Mon Sep 17 00:00:00 2001 From: Xichen Lin Date: Sun, 19 Jul 2026 21:26:35 +1000 Subject: [PATCH 2/2] [202605][intfsorch]: Retry explicit interface VRF rehome Retain an explicit vrf_name SET until prefixes and RIF references drain, then replace the RIF in the requested VRF. Preserve interface attributes, block replacement prefix additions, and roll back to the old RIF if replacement creation fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b6b568a1-5d2a-4309-b1ac-ef21ca155079 Signed-off-by: Xichen Lin --- orchagent/intfsorch.cpp | 131 ++++++++++++++++++--- orchagent/intfsorch.h | 5 +- tests/mock_tests/intfsorch_ut.cpp | 186 ++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 20 deletions(-) diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 256a72ed7d5..58d67f02e23 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -118,7 +118,8 @@ sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias) sai_object_id_t IntfsOrch::getRouterIntfsIdForNewDependency(const string &alias) { - if (m_removingIntfses.find(alias) != m_removingIntfses.end()) + if (m_removingIntfses.find(alias) != m_removingIntfses.end() || + m_pendingVrfUpdates.find(alias) != m_pendingVrfUpdates.end()) { return SAI_NULL_OBJECT_ID; } @@ -487,7 +488,8 @@ set IntfsOrch:: getSubnetRoutes() } bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix, - const bool adminUp, const uint32_t mtu, string loopbackAction) + const bool adminUp, const uint32_t mtu, string loopbackAction, + const bool vrfIdIsExplicit) { SWSS_LOG_ENTER(); @@ -497,6 +499,11 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre return false; } + if (ip_prefix && m_pendingVrfUpdates.find(alias) != m_pendingVrfUpdates.end()) + { + return false; + } + Port port; gPortsOrch->getPort(alias, port); @@ -510,8 +517,18 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre intfs_entry.ref_count = 0; intfs_entry.proxy_arp = false; intfs_entry.vrf_id = vrf_id; + if (port.m_mac) + { + intfs_entry.mac = port.m_mac; + } + else + { + intfs_entry.mac = gMacAddress; + } + intfs_entry.loopback_action = loopbackAction; m_syncdIntfses[alias] = intfs_entry; m_vrfOrch->increaseVrfRefCount(vrf_id); + m_pendingVrfUpdates.erase(alias); } else { @@ -520,30 +537,94 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre } else { - if (!ip_prefix && port.m_type == Port::SUBPORT) + // Treat an explicit VRF change as a RIF replacement and retain the SET + // until the old interface has no prefixes or dependent objects. + sai_object_id_t old_vrf_id = it_intfs->second.vrf_id; + if (!ip_prefix && vrfIdIsExplicit && old_vrf_id != vrf_id) { - // port represents a sub interface - // Change sub interface config at run time - bool attrChanged = false; - if (mtu && port.m_mtu != mtu) + m_pendingVrfUpdates.insert(alias); + if (!it_intfs->second.ip_addresses.empty()) { - port.m_mtu = mtu; - attrChanged = true; + return false; + } - setRouterIntfsMtu(port); + IntfsEntry intfs_entry = it_intfs->second; + Port rehome_port = port; + rehome_port.m_mac = intfs_entry.mac; + if (rehome_port.m_type == Port::SUBPORT) + { + rehome_port.m_admin_state_up = adminUp; + if (mtu) + { + rehome_port.m_mtu = mtu; + } + } + if (!removeRouterIntfs(port)) + { + return false; } - if (port.m_admin_state_up != adminUp) + rehome_port.m_rif_id = SAI_NULL_OBJECT_ID; + rehome_port.m_vr_id = SAI_NULL_OBJECT_ID; + string rehome_loopback_action = loopbackAction.empty() ? + intfs_entry.loopback_action : + loopbackAction; + try { - port.m_admin_state_up = adminUp; - attrChanged = true; + if (!addRouterIntfs(vrf_id, rehome_port, rehome_loopback_action)) + { + return false; + } + } + catch (const std::exception&) + { + Port rollback_port = rehome_port; + rollback_port.m_rif_id = SAI_NULL_OBJECT_ID; + rollback_port.m_vr_id = SAI_NULL_OBJECT_ID; + addRouterIntfs(old_vrf_id, rollback_port, intfs_entry.loopback_action); + throw; + } - setRouterIntfsAdminStatus(port); + m_vrfOrch->decreaseVrfRefCount(old_vrf_id); + m_vrfOrch->increaseVrfRefCount(vrf_id); + intfs_entry.vrf_id = vrf_id; + intfs_entry.loopback_action = rehome_loopback_action; + m_syncdIntfses[alias] = intfs_entry; + + m_pendingVrfUpdates.erase(alias); + } + else if (!ip_prefix) + { + if (vrfIdIsExplicit) + { + m_pendingVrfUpdates.erase(alias); } - if (attrChanged) + if (port.m_type == Port::SUBPORT) { - gPortsOrch->setPort(alias, port); + // port represents a sub interface + // Change sub interface config at run time + bool attrChanged = false; + if (mtu && port.m_mtu != mtu) + { + port.m_mtu = mtu; + attrChanged = true; + + setRouterIntfsMtu(port); + } + + if (port.m_admin_state_up != adminUp) + { + port.m_admin_state_up = adminUp; + attrChanged = true; + + setRouterIntfsAdminStatus(port); + } + + if (attrChanged) + { + gPortsOrch->setPort(alias, port); + } } } } @@ -648,6 +729,7 @@ bool IntfsOrch::removeIntf(const string& alias, sai_object_id_t vrf_id, const Ip gPortsOrch->decreasePortRefCount(alias); m_syncdIntfses.erase(alias); m_vrfOrch->decreaseVrfRefCount(vrf_id); + m_pendingVrfUpdates.erase(alias); if (port.m_type == Port::SUBPORT) { @@ -715,6 +797,8 @@ void IntfsOrch::doTask(Consumer &consumer) const vector& data = kfvFieldsValues(t); string vrf_name = "", vnet_name = "", nat_zone = ""; + bool vrfNameIsExplicit = false; + bool mplsIsExplicit = false; MacAddress mac; uint32_t mtu = 0; @@ -734,6 +818,7 @@ void IntfsOrch::doTask(Consumer &consumer) if (field == "vrf_name") { vrf_name = value; + vrfNameIsExplicit = true; } else if (field == "vnet_name") { @@ -754,6 +839,7 @@ void IntfsOrch::doTask(Consumer &consumer) else if (field == "mpls") { mpls = (value == "enable" ? true : false); + mplsIsExplicit = true; } else if (field == "nat_zone") { @@ -972,7 +1058,8 @@ void IntfsOrch::doTask(Consumer &consumer) adminUp = port.m_admin_state_up; } - if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu, loopbackAction)) + if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu, + loopbackAction, vrfNameIsExplicit)) { it++; continue; @@ -997,7 +1084,7 @@ void IntfsOrch::doTask(Consumer &consumer) gPortsOrch->setPort(alias, port); } /* Set MPLS */ - if ((!ip_prefix_in_key) && (port.m_mpls != mpls)) + if (mplsIsExplicit && !ip_prefix_in_key && port.m_mpls != mpls) { port.m_mpls = mpls; @@ -1008,7 +1095,10 @@ void IntfsOrch::doTask(Consumer &consumer) /* Set loopback action */ if (!loopbackAction.empty()) { - setIntfLoopbackAction(port, loopbackAction); + if (setIntfLoopbackAction(port, loopbackAction)) + { + m_syncdIntfses[alias].loopback_action = loopbackAction; + } } } } @@ -1038,6 +1128,9 @@ void IntfsOrch::doTask(Consumer &consumer) { SWSS_LOG_NOTICE("Set router interface mac %s for port %s success", mac.to_string().c_str(), port.m_alias.c_str()); + m_syncdIntfses[alias].mac = mac; + port.m_mac = mac; + gPortsOrch->setPort(alias, port); } } else diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index fa1b9088cc6..10940fd883b 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -26,6 +26,8 @@ struct IntfsEntry int ref_count; sai_object_id_t vrf_id; bool proxy_arp; + MacAddress mac; + std::string loopback_action; }; typedef map IntfsTable; @@ -58,7 +60,7 @@ class IntfsOrch : public Orch bool setIntfLoopbackAction(const Port &port, string actionStr); bool getSaiLoopbackAction(const string &actionStr, sai_packet_action_t &action); - bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0, string loopbackAction = ""); + bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0, string loopbackAction = "", const bool vrfIdIsExplicit = false); bool removeIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr); void addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix); @@ -93,6 +95,7 @@ class IntfsOrch : public Orch unique_ptr m_vidToRidTable; std::set m_removingIntfses; + std::set m_pendingVrfUpdates; std::string getRifFlexCounterTableKey(std::string s); diff --git a/tests/mock_tests/intfsorch_ut.cpp b/tests/mock_tests/intfsorch_ut.cpp index 9e1f65100ae..d8b64c0b37f 100644 --- a/tests/mock_tests/intfsorch_ut.cpp +++ b/tests/mock_tests/intfsorch_ut.cpp @@ -16,6 +16,9 @@ namespace intfsorch_test int create_rif_count = 0; int remove_rif_count = 0; + bool saw_loopback_action = false; + bool fail_next_rif_create = false; + sai_packet_action_t last_loopback_action = SAI_PACKET_ACTION_FORWARD; sai_router_interface_api_t *pold_sai_rif_api; sai_router_interface_api_t ut_sai_rif_api; @@ -26,6 +29,20 @@ namespace intfsorch_test _In_ const sai_attribute_t *attr_list) { ++create_rif_count; + if (fail_next_rif_create) + { + fail_next_rif_create = false; + return SAI_STATUS_INSUFFICIENT_RESOURCES; + } + *router_interface_id = 0x100000 + create_rif_count; + for (uint32_t i = 0; i < attr_count; ++i) + { + if (attr_list[i].id == SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION) + { + saw_loopback_action = true; + last_loopback_action = static_cast(attr_list[i].value.s32); + } + } return SAI_STATUS_SUCCESS; } @@ -61,6 +78,8 @@ namespace intfsorch_test sai_router_intfs_api->create_router_interface = _ut_create_router_interface; sai_router_intfs_api->remove_router_interface = _ut_remove_router_interface; + saw_loopback_action = false; + fail_next_rif_create = false; m_app_db = make_shared("APPL_DB", 0); m_config_db = make_shared("CONFIG_DB", 0); @@ -408,4 +427,171 @@ namespace intfsorch_test m_syncdIntfses = gIntfsOrch->getSyncdIntfses(); ASSERT_EQ(m_syncdIntfses["Loopback3"].vrf_id, gVirtualRouterId); } + + TEST_F(IntfsOrchTest, IntfsOrchVrfUpdateWaitsForDrain) + { + std::deque entries{ + {"Vrf-Blue", "SET", {{"NULL", "NULL"}}} + }; + auto consumer = dynamic_cast(gVrfOrch->getExecutor(APP_VRF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gVrfOrch)->doTask(); + + entries = { + {"Ethernet0", "SET", { + {"mtu", "9100"}, + {"loopback_action", "drop"}, + {"mpls", "enable"}, + {"mac_addr", "00:11:22:33:44:55"} + }}, + {"Ethernet0:10.0.0.1/24", "SET", {{"scope", "global"}, {"family", "IPv4"}}} + }; + consumer = dynamic_cast(gIntfsOrch->getExecutor(APP_INTF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + Port port; + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", port)); + port.m_rif_id = 0x123456; + port.m_vr_id = gVirtualRouterId; + gPortsOrch->setPort("Ethernet0", port); + gIntfsOrch->increaseRouterIntfsRefCount("Ethernet0"); + + entries = { + {"Ethernet0", "SET", {{"vrf_name", "Vrf-Blue"}}}, + {"Ethernet0:10.0.0.1/24", "DEL", {}}, + {"Ethernet0:10.0.0.1/24", "SET", {{"scope", "global"}, {"family", "IPv4"}}} + }; + saw_loopback_action = false; + consumer->addToSync(entries); + + auto create_count = create_rif_count; + auto remove_count = remove_rif_count; + static_cast(gIntfsOrch)->doTask(); + ASSERT_EQ(consumer->m_toSync.size(), 2u); + ASSERT_EQ(create_count, create_rif_count); + ASSERT_EQ(remove_count, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getRouterIntfsIdForNewDependency("Ethernet0"), SAI_NULL_OBJECT_ID); + + static_cast(gIntfsOrch)->doTask(); + ASSERT_EQ(consumer->m_toSync.size(), 2u); + ASSERT_EQ(remove_count, remove_rif_count); + + gIntfsOrch->decreaseRouterIntfsRefCount("Ethernet0"); + static_cast(gIntfsOrch)->doTask(); + + ASSERT_TRUE(consumer->m_toSync.empty()); + ASSERT_EQ(create_count + 1, create_rif_count); + ASSERT_EQ(remove_count + 1, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0").vrf_id, + gVrfOrch->getVRFid("Vrf-Blue")); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0").ip_addresses.count( + IpPrefix("10.0.0.1/24")), + 1u); + ASSERT_TRUE(saw_loopback_action); + ASSERT_EQ(last_loopback_action, SAI_PACKET_ACTION_DROP); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0", port)); + ASSERT_TRUE(port.m_mpls); + ASSERT_EQ(port.m_mac, MacAddress("00:11:22:33:44:55")); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0").mac, + MacAddress("00:11:22:33:44:55")); + } + + TEST_F(IntfsOrchTest, IntfsOrchPartialUpdatePreservesVrf) + { + std::deque entries{ + {"Vrf-Blue", "SET", {{"NULL", "NULL"}}} + }; + auto consumer = dynamic_cast(gVrfOrch->getExecutor(APP_VRF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gVrfOrch)->doTask(); + + entries = { + {"Ethernet0.10", "SET", { + {"vlan", "10"}, + {"admin_status", "up"}, + {"mtu", "9100"} + }} + }; + consumer = dynamic_cast(gIntfsOrch->getExecutor(APP_INTF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + auto create_count = create_rif_count; + auto remove_count = remove_rif_count; + entries = { + {"Ethernet0.10", "SET", { + {"vrf_name", "Vrf-Blue"}, + {"admin_status", "down"}, + {"mtu", "1500"} + }} + }; + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + ASSERT_EQ(create_count + 1, create_rif_count); + ASSERT_EQ(remove_count + 1, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0.10").vrf_id, + gVrfOrch->getVRFid("Vrf-Blue")); + + Port port; + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0.10", port)); + ASSERT_FALSE(port.m_admin_state_up); + ASSERT_EQ(port.m_mtu, 1500u); + + create_count = create_rif_count; + remove_count = remove_rif_count; + entries = { + {"Ethernet0.10", "SET", {{"admin_status", "up"}}} + }; + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + ASSERT_EQ(create_count, create_rif_count); + ASSERT_EQ(remove_count, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0.10").vrf_id, + gVrfOrch->getVRFid("Vrf-Blue")); + ASSERT_TRUE(gPortsOrch->getPort("Ethernet0.10", port)); + ASSERT_TRUE(port.m_admin_state_up); + } + + TEST_F(IntfsOrchTest, IntfsOrchVrfUpdateRollsBackCreateFailure) + { + std::deque entries{ + {"Vrf-Blue", "SET", {{"NULL", "NULL"}}} + }; + auto consumer = dynamic_cast(gVrfOrch->getExecutor(APP_VRF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gVrfOrch)->doTask(); + + entries = { + {"Ethernet0", "SET", {{"mtu", "9100"}, {"loopback_action", "drop"}}} + }; + consumer = dynamic_cast(gIntfsOrch->getExecutor(APP_INTF_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + auto create_count = create_rif_count; + auto remove_count = remove_rif_count; + fail_next_rif_create = true; + entries = { + {"Ethernet0", "SET", {{"vrf_name", "Vrf-Blue"}}} + }; + consumer->addToSync(entries); + static_cast(gIntfsOrch)->doTask(); + + ASSERT_EQ(consumer->m_toSync.size(), 1u); + ASSERT_EQ(create_count + 2, create_rif_count); + ASSERT_EQ(remove_count + 1, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0").vrf_id, gVirtualRouterId); + ASSERT_EQ(gIntfsOrch->getRouterIntfsIdForNewDependency("Ethernet0"), SAI_NULL_OBJECT_ID); + + static_cast(gIntfsOrch)->doTask(); + + ASSERT_TRUE(consumer->m_toSync.empty()); + ASSERT_EQ(create_count + 3, create_rif_count); + ASSERT_EQ(remove_count + 2, remove_rif_count); + ASSERT_EQ(gIntfsOrch->getSyncdIntfses().at("Ethernet0").vrf_id, + gVrfOrch->getVRFid("Vrf-Blue")); + } } \ No newline at end of file