Skip to content

Commit 6e288dc

Browse files
authored
New P4Orch development. (#2425)
Upstream new development on p4orch: *Reference count bug fix in wcmp manager. *New update in gre tunnel manager. *Bulk support in wcmp group member.
1 parent ab0e474 commit 6e288dc

14 files changed

+1484
-752
lines changed

orchagent/p4orch/gre_tunnel_manager.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,10 @@ std::vector<sai_attribute_t> getSaiAttrs(const P4GreTunnelEntry &gre_tunnel_entr
8989
} // namespace
9090

9191
P4GreTunnelEntry::P4GreTunnelEntry(const std::string &tunnel_id, const std::string &router_interface_id,
92-
const swss::IpAddress &encap_src_ip, const swss::IpAddress &encap_dst_ip)
92+
const swss::IpAddress &encap_src_ip, const swss::IpAddress &encap_dst_ip,
93+
const swss::IpAddress &neighbor_id)
9394
: tunnel_id(tunnel_id), router_interface_id(router_interface_id), encap_src_ip(encap_src_ip),
94-
encap_dst_ip(encap_dst_ip)
95+
encap_dst_ip(encap_dst_ip), neighbor_id(neighbor_id)
9596
{
9697
SWSS_LOG_ENTER();
9798
tunnel_key = KeyGenerator::generateTunnelKey(tunnel_id);
@@ -188,7 +189,7 @@ P4GreTunnelEntry *GreTunnelManager::getGreTunnelEntry(const std::string &tunnel_
188189
}
189190
};
190191

191-
ReturnCodeOr<const std::string> GreTunnelManager::getUnderlayIfFromGreTunnelEntry(const std::string &tunnel_key)
192+
ReturnCodeOr<const P4GreTunnelEntry> GreTunnelManager::getConstGreTunnelEntry(const std::string &tunnel_key)
192193
{
193194
SWSS_LOG_ENTER();
194195

@@ -200,7 +201,7 @@ ReturnCodeOr<const std::string> GreTunnelManager::getUnderlayIfFromGreTunnelEntr
200201
}
201202
else
202203
{
203-
return tunnel->router_interface_id;
204+
return *tunnel;
204205
}
205206
}
206207

@@ -274,7 +275,7 @@ ReturnCode GreTunnelManager::processAddRequest(const P4GreTunnelAppDbEntry &app_
274275
SWSS_LOG_ENTER();
275276

276277
P4GreTunnelEntry gre_tunnel_entry(app_db_entry.tunnel_id, app_db_entry.router_interface_id,
277-
app_db_entry.encap_src_ip, app_db_entry.encap_dst_ip);
278+
app_db_entry.encap_src_ip, app_db_entry.encap_dst_ip, app_db_entry.encap_dst_ip);
278279
auto status = createGreTunnel(gre_tunnel_entry);
279280
if (!status.ok())
280281
{
@@ -570,6 +571,15 @@ std::string GreTunnelManager::verifyStateCache(const P4GreTunnelAppDbEntry &app_
570571
return msg.str();
571572
}
572573

574+
if (gre_tunnel_entry->neighbor_id.to_string() != app_db_entry.encap_dst_ip.to_string())
575+
{
576+
std::stringstream msg;
577+
msg << "GreTunnel " << QuotedVar(app_db_entry.tunnel_id) << " with destination IP "
578+
<< QuotedVar(app_db_entry.encap_dst_ip.to_string()) << " does not match internal cache "
579+
<< QuotedVar(gre_tunnel_entry->neighbor_id.to_string()) << " fo neighbor_id in GreTunnel manager.";
580+
return msg.str();
581+
}
582+
573583
return m_p4OidMapper->verifyOIDMapping(SAI_OBJECT_TYPE_TUNNEL, gre_tunnel_entry->tunnel_key,
574584
gre_tunnel_entry->tunnel_oid);
575585
}
@@ -616,4 +626,4 @@ std::string GreTunnelManager::verifyStateAsicDb(const P4GreTunnelEntry *gre_tunn
616626

617627
return verifyAttrs(values, exp, std::vector<swss::FieldValueTuple>{},
618628
/*allow_unknown=*/false);
619-
}
629+
}

orchagent/p4orch/gre_tunnel_manager.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ struct P4GreTunnelEntry
3636
std::string router_interface_id;
3737
swss::IpAddress encap_src_ip;
3838
swss::IpAddress encap_dst_ip;
39+
// neighbor_id is required to be equal to encap_dst_ip by BRCM. And the
40+
// neighbor entry needs to be created before GRE tunnel object
41+
swss::IpAddress neighbor_id;
3942

4043
// SAI OID associated with this entry.
4144
sai_object_id_t tunnel_oid = SAI_NULL_OBJECT_ID;
@@ -45,7 +48,8 @@ struct P4GreTunnelEntry
4548
sai_object_id_t underlay_if_oid = SAI_NULL_OBJECT_ID;
4649

4750
P4GreTunnelEntry(const std::string &tunnel_id, const std::string &router_interface_id,
48-
const swss::IpAddress &encap_src_ip, const swss::IpAddress &encap_dst_ip);
51+
const swss::IpAddress &encap_src_ip, const swss::IpAddress &encap_dst_ip,
52+
const swss::IpAddress &neighbor_id);
4953
};
5054

5155
// GreTunnelManager listens to changes in table APP_P4RT_TUNNEL_TABLE_NAME and
@@ -69,7 +73,7 @@ class GreTunnelManager : public ObjectManagerInterface
6973
void drain() override;
7074
std::string verifyState(const std::string &key, const std::vector<swss::FieldValueTuple> &tuple) override;
7175

72-
ReturnCodeOr<const std::string> getUnderlayIfFromGreTunnelEntry(const std::string &gre_tunnel_key);
76+
ReturnCodeOr<const P4GreTunnelEntry> getConstGreTunnelEntry(const std::string &gre_tunnel_key);
7377

7478
private:
7579
// Gets the internal cached GRE tunnel entry by its key.

orchagent/p4orch/next_hop_manager.cpp

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,22 @@ namespace
4141

4242
ReturnCode validateAppDbEntry(const P4NextHopAppDbEntry &app_db_entry)
4343
{
44+
// TODO(b/225242372): remove kSetNexthop action after P4RT and Orion update
45+
// naming
4446
if (app_db_entry.action_str != p4orch::kSetIpNexthop && app_db_entry.action_str != p4orch::kSetNexthop &&
4547
app_db_entry.action_str != p4orch::kSetTunnelNexthop)
4648
{
4749
return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM)
4850
<< "Invalid action " << QuotedVar(app_db_entry.action_str) << " of Nexthop App DB entry";
4951
}
50-
if (app_db_entry.neighbor_id.isZero())
52+
if (app_db_entry.action_str == p4orch::kSetIpNexthop && app_db_entry.neighbor_id.isZero())
5153
{
5254
return ReturnCode(StatusCode::SWSS_RC_INVALID_PARAM)
5355
<< "Missing field " << QuotedVar(prependParamField(p4orch::kNeighborId)) << " for action "
5456
<< QuotedVar(p4orch::kSetIpNexthop) << " in table entry";
5557
}
58+
// TODO(b/225242372): remove kSetNexthop action after P4RT and Orion update
59+
// naming
5660
if (app_db_entry.action_str == p4orch::kSetIpNexthop || app_db_entry.action_str == p4orch::kSetNexthop)
5761
{
5862
if (!app_db_entry.gre_tunnel_id.empty())
@@ -321,23 +325,27 @@ ReturnCode NextHopManager::createNextHop(P4NextHopEntry &next_hop_entry)
321325
<< " already exists in centralized mapper");
322326
}
323327

324-
std::string router_interface_id = next_hop_entry.router_interface_id;
325328
if (!next_hop_entry.gre_tunnel_id.empty())
326329
{
327-
auto underlay_if_or = gP4Orch->getGreTunnelManager()->getUnderlayIfFromGreTunnelEntry(
330+
auto gre_tunnel_or = gP4Orch->getGreTunnelManager()->getConstGreTunnelEntry(
328331
KeyGenerator::generateTunnelKey(next_hop_entry.gre_tunnel_id));
329-
if (!underlay_if_or.ok())
332+
if (!gre_tunnel_or.ok())
330333
{
331334
LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_NOT_FOUND)
332335
<< "GRE Tunnel " << QuotedVar(next_hop_entry.gre_tunnel_id)
333336
<< " does not exist in GRE Tunnel Manager");
334337
}
335-
router_interface_id = *underlay_if_or;
338+
next_hop_entry.router_interface_id = (*gre_tunnel_or).router_interface_id;
339+
// BRCM requires neighbor object to be created before GRE tunnel, referring
340+
// to the one in GRE tunnel object when creating next_hop_entry_with
341+
// setTunnelAction
342+
next_hop_entry.neighbor_id = (*gre_tunnel_or).neighbor_id;
336343
}
337344

338345
// Neighbor doesn't have OID and the IP addr needed in next hop creation is
339346
// neighbor_id, so only check neighbor existence in centralized mapper.
340-
const auto neighbor_key = KeyGenerator::generateNeighborKey(router_interface_id, next_hop_entry.neighbor_id);
347+
const auto neighbor_key =
348+
KeyGenerator::generateNeighborKey(next_hop_entry.router_interface_id, next_hop_entry.neighbor_id);
341349
if (!m_p4OidMapper->existsOID(SAI_OBJECT_TYPE_NEIGHBOR_ENTRY, neighbor_key))
342350
{
343351
LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_NOT_FOUND)
@@ -456,15 +464,15 @@ ReturnCode NextHopManager::removeNextHop(const std::string &next_hop_key)
456464
std::string router_interface_id = next_hop_entry->router_interface_id;
457465
if (!next_hop_entry->gre_tunnel_id.empty())
458466
{
459-
auto underlay_if_or = gP4Orch->getGreTunnelManager()->getUnderlayIfFromGreTunnelEntry(
467+
auto gre_tunnel_or = gP4Orch->getGreTunnelManager()->getConstGreTunnelEntry(
460468
KeyGenerator::generateTunnelKey(next_hop_entry->gre_tunnel_id));
461-
if (!underlay_if_or.ok())
469+
if (!gre_tunnel_or.ok())
462470
{
463471
LOG_ERROR_AND_RETURN(ReturnCode(StatusCode::SWSS_RC_NOT_FOUND)
464472
<< "GRE Tunnel " << QuotedVar(next_hop_entry->gre_tunnel_id)
465473
<< " does not exist in GRE Tunnel Manager");
466474
}
467-
router_interface_id = *underlay_if_or;
475+
router_interface_id = (*gre_tunnel_or).router_interface_id;
468476
}
469477
m_p4OidMapper->decreaseRefCount(
470478
SAI_OBJECT_TYPE_NEIGHBOR_ENTRY,
@@ -560,15 +568,17 @@ std::string NextHopManager::verifyStateCache(const P4NextHopAppDbEntry &app_db_e
560568
<< QuotedVar(next_hop_entry->next_hop_id) << " in nexthop manager.";
561569
return msg.str();
562570
}
563-
if (next_hop_entry->router_interface_id != app_db_entry.router_interface_id)
571+
if (app_db_entry.action_str == p4orch::kSetIpNexthop &&
572+
next_hop_entry->router_interface_id != app_db_entry.router_interface_id)
564573
{
565574
std::stringstream msg;
566575
msg << "Nexthop " << QuotedVar(app_db_entry.next_hop_id) << " with ritf ID "
567576
<< QuotedVar(app_db_entry.router_interface_id) << " does not match internal cache "
568577
<< QuotedVar(next_hop_entry->router_interface_id) << " in nexthop manager.";
569578
return msg.str();
570579
}
571-
if (next_hop_entry->neighbor_id.to_string() != app_db_entry.neighbor_id.to_string())
580+
if (app_db_entry.action_str == p4orch::kSetIpNexthop &&
581+
next_hop_entry->neighbor_id.to_string() != app_db_entry.neighbor_id.to_string())
572582
{
573583
std::stringstream msg;
574584
msg << "Nexthop " << QuotedVar(app_db_entry.next_hop_id) << " with neighbor ID "
@@ -577,14 +587,45 @@ std::string NextHopManager::verifyStateCache(const P4NextHopAppDbEntry &app_db_e
577587
return msg.str();
578588
}
579589

580-
if (next_hop_entry->gre_tunnel_id != app_db_entry.gre_tunnel_id)
590+
if (app_db_entry.action_str == p4orch::kSetTunnelNexthop &&
591+
next_hop_entry->gre_tunnel_id != app_db_entry.gre_tunnel_id)
581592
{
582593
std::stringstream msg;
583594
msg << "Nexthop " << QuotedVar(app_db_entry.next_hop_id) << " with GRE tunnel ID "
584595
<< QuotedVar(app_db_entry.gre_tunnel_id) << " does not match internal cache "
585596
<< QuotedVar(next_hop_entry->gre_tunnel_id) << " in nexthop manager.";
586597
return msg.str();
587598
}
599+
if (!next_hop_entry->gre_tunnel_id.empty())
600+
{
601+
auto gre_tunnel_or = gP4Orch->getGreTunnelManager()->getConstGreTunnelEntry(
602+
KeyGenerator::generateTunnelKey(next_hop_entry->gre_tunnel_id));
603+
if (!gre_tunnel_or.ok())
604+
{
605+
std::stringstream msg;
606+
msg << "GRE Tunnel " << QuotedVar(next_hop_entry->gre_tunnel_id) << " does not exist in GRE Tunnel Manager";
607+
return msg.str();
608+
}
609+
P4GreTunnelEntry gre_tunnel = *gre_tunnel_or;
610+
if (gre_tunnel.neighbor_id.to_string() != next_hop_entry->neighbor_id.to_string())
611+
{
612+
std::stringstream msg;
613+
msg << "Nexthop " << QuotedVar(next_hop_entry->next_hop_id) << " with neighbor ID "
614+
<< QuotedVar(next_hop_entry->neighbor_id.to_string())
615+
<< " in nexthop manager does not match internal cache " << QuotedVar(gre_tunnel.neighbor_id.to_string())
616+
<< " with tunnel ID " << QuotedVar(gre_tunnel.tunnel_id) << " in GRE tunnel manager.";
617+
return msg.str();
618+
}
619+
if (gre_tunnel.router_interface_id != next_hop_entry->router_interface_id)
620+
{
621+
std::stringstream msg;
622+
msg << "Nexthop " << QuotedVar(next_hop_entry->next_hop_id) << " with rif ID "
623+
<< QuotedVar(next_hop_entry->router_interface_id)
624+
<< " in nexthop manager does not match internal cache " << QuotedVar(gre_tunnel.router_interface_id)
625+
<< " with tunnel ID " << QuotedVar(gre_tunnel.tunnel_id) << " in GRE tunnel manager.";
626+
return msg.str();
627+
}
628+
}
588629

589630
return m_p4OidMapper->verifyOIDMapping(SAI_OBJECT_TYPE_NEXT_HOP, next_hop_entry->next_hop_key,
590631
next_hop_entry->next_hop_oid);

orchagent/p4orch/p4orch.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ P4Orch::P4Orch(swss::DBConnector *db, std::vector<std::string> tableNames, VRFOr
3030
SWSS_LOG_ENTER();
3131

3232
m_routerIntfManager = std::make_unique<RouterInterfaceManager>(&m_p4OidMapper, &m_publisher);
33-
m_greTunnelManager = std::make_unique<GreTunnelManager>(&m_p4OidMapper, &m_publisher);
3433
m_neighborManager = std::make_unique<NeighborManager>(&m_p4OidMapper, &m_publisher);
34+
m_greTunnelManager = std::make_unique<GreTunnelManager>(&m_p4OidMapper, &m_publisher);
3535
m_nextHopManager = std::make_unique<NextHopManager>(&m_p4OidMapper, &m_publisher);
3636
m_routeManager = std::make_unique<RouteManager>(&m_p4OidMapper, vrfOrch, &m_publisher);
3737
m_mirrorSessionManager = std::make_unique<p4orch::MirrorSessionManager>(&m_p4OidMapper, &m_publisher);
@@ -52,8 +52,8 @@ P4Orch::P4Orch(swss::DBConnector *db, std::vector<std::string> tableNames, VRFOr
5252
m_p4TableToManagerMap[APP_P4RT_L3_ADMIT_TABLE_NAME] = m_l3AdmitManager.get();
5353

5454
m_p4ManagerPrecedence.push_back(m_routerIntfManager.get());
55-
m_p4ManagerPrecedence.push_back(m_greTunnelManager.get());
5655
m_p4ManagerPrecedence.push_back(m_neighborManager.get());
56+
m_p4ManagerPrecedence.push_back(m_greTunnelManager.get());
5757
m_p4ManagerPrecedence.push_back(m_nextHopManager.get());
5858
m_p4ManagerPrecedence.push_back(m_wcmpManager.get());
5959
m_p4ManagerPrecedence.push_back(m_routeManager.get());

orchagent/p4orch/p4orch_util.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ constexpr char *kSetWcmpGroupIdAndMetadata = "set_wcmp_group_id_and_metadata";
4040
constexpr char *kSetMetadataAndDrop = "set_metadata_and_drop";
4141
constexpr char *kSetNexthop = "set_nexthop";
4242
constexpr char *kSetIpNexthop = "set_ip_nexthop";
43-
constexpr char *kSetTunnelNexthop = "set_tunnel_encap_nexthop";
43+
constexpr char *kSetTunnelNexthop = "set_p2p_tunnel_encap_nexthop";
4444
constexpr char *kDrop = "drop";
4545
constexpr char *kTrap = "trap";
4646
constexpr char *kStage = "stage";
@@ -79,7 +79,7 @@ constexpr char *kTtl = "ttl";
7979
constexpr char *kTos = "tos";
8080
constexpr char *kMirrorAsIpv4Erspan = "mirror_as_ipv4_erspan";
8181
constexpr char *kL3AdmitAction = "admit_to_l3";
82-
constexpr char *kTunnelAction = "mark_for_tunnel_encap";
82+
constexpr char *kTunnelAction = "mark_for_p2p_tunnel_encap";
8383
} // namespace p4orch
8484

8585
// Prepends "match/" to the input string str to construct a new string.

orchagent/p4orch/tests/gre_tunnel_manager_test.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const P4GreTunnelAppDbEntry kP4GreTunnelAppDbEntry1{/*tunnel_id=*/"tunnel-1",
5252
/*router_interface_id=*/"intf-eth-1/2/3",
5353
/*encap_src_ip=*/swss::IpAddress("2607:f8b0:8096:3110::1"),
5454
/*encap_dst_ip=*/swss::IpAddress("2607:f8b0:8096:311a::2"),
55-
/*action_str=*/"mark_for_tunnel_encap"};
55+
/*action_str=*/"mark_for_p2p_tunnel_encap"};
5656

5757
std::unordered_map<sai_attr_id_t, sai_attribute_value_t> CreateAttributeListForGreTunnelObject(
5858
const P4GreTunnelAppDbEntry &app_entry, const sai_object_id_t &rif_oid)
@@ -304,6 +304,7 @@ bool GreTunnelManagerTest::ValidateGreTunnelEntryAdd(const P4GreTunnelAppDbEntry
304304
const auto *p4_gre_tunnel_entry = GetGreTunnelEntry(KeyGenerator::generateTunnelKey(app_db_entry.tunnel_id));
305305
if (p4_gre_tunnel_entry == nullptr || p4_gre_tunnel_entry->encap_src_ip != app_db_entry.encap_src_ip ||
306306
p4_gre_tunnel_entry->encap_dst_ip != app_db_entry.encap_dst_ip ||
307+
p4_gre_tunnel_entry->neighbor_id != app_db_entry.encap_dst_ip ||
307308
p4_gre_tunnel_entry->router_interface_id != app_db_entry.router_interface_id ||
308309
p4_gre_tunnel_entry->tunnel_id != app_db_entry.tunnel_id)
309310
{
@@ -334,7 +335,7 @@ TEST_F(GreTunnelManagerTest, ProcessAddRequestShouldFailWhenDependingPortIsNotPr
334335
/*router_interface_id=*/"intf-eth-1/2/3",
335336
/*encap_src_ip=*/swss::IpAddress("2607:f8b0:8096:3110::1"),
336337
/*encap_dst_ip=*/swss::IpAddress("2607:f8b0:8096:311a::2"),
337-
/*action_str=*/"mark_for_tunnel_encap"};
338+
/*action_str=*/"mark_for_p2p_tunnel_encap"};
338339
const auto gre_tunnel_key = KeyGenerator::generateTunnelKey(kAppDbEntry.tunnel_id);
339340

340341
EXPECT_EQ(StatusCode::SWSS_RC_NOT_FOUND, ProcessAddRequest(kAppDbEntry));
@@ -817,6 +818,12 @@ TEST_F(GreTunnelManagerTest, VerifyStateTest)
817818
EXPECT_FALSE(VerifyState(db_key, attributes).empty());
818819
p4_tunnel_entry->encap_dst_ip = saved_DST_IP;
819820

821+
// Verification should fail if IP mask mismatches.
822+
auto saved_NEIGHBOR_ID = p4_tunnel_entry->neighbor_id;
823+
p4_tunnel_entry->neighbor_id = swss::IpAddress("2.2.2.2");
824+
EXPECT_FALSE(VerifyState(db_key, attributes).empty());
825+
p4_tunnel_entry->neighbor_id = saved_NEIGHBOR_ID;
826+
820827
// Verification should fail if tunnel_id mismatches.
821828
auto saved_tunnel_id = p4_tunnel_entry->tunnel_id;
822829
p4_tunnel_entry->tunnel_id = "invalid";

orchagent/p4orch/tests/mock_sai_next_hop_group.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
extern "C"
77
{
88
#include "sai.h"
9+
#include "sainexthopgroup.h"
910
}
1011

1112
// Mock class including mock functions mapping to SAI next hop group's
@@ -27,6 +28,16 @@ class MockSaiNextHopGroup
2728

2829
MOCK_METHOD2(set_next_hop_group_member_attribute,
2930
sai_status_t(_In_ sai_object_id_t next_hop_group_member_id, _In_ const sai_attribute_t *attr));
31+
32+
MOCK_METHOD7(create_next_hop_group_members,
33+
sai_status_t(_In_ sai_object_id_t switch_id, _In_ uint32_t object_count,
34+
_In_ const uint32_t *attr_count, _In_ const sai_attribute_t **attr_list,
35+
_In_ sai_bulk_op_error_mode_t mode, _Out_ sai_object_id_t *object_id,
36+
_Out_ sai_status_t *object_statuses));
37+
38+
MOCK_METHOD4(remove_next_hop_group_members,
39+
sai_status_t(_In_ uint32_t object_count, _In_ const sai_object_id_t *object_id,
40+
_In_ sai_bulk_op_error_mode_t mode, _Out_ sai_status_t *object_statuses));
3041
};
3142

3243
// Note that before mock functions below are used, mock_sai_next_hop_group must
@@ -62,3 +73,18 @@ sai_status_t set_next_hop_group_member_attribute(_In_ sai_object_id_t next_hop_g
6273
{
6374
return mock_sai_next_hop_group->set_next_hop_group_member_attribute(next_hop_group_member_id, attr);
6475
}
76+
77+
sai_status_t create_next_hop_group_members(_In_ sai_object_id_t switch_id, _In_ uint32_t object_count,
78+
_In_ const uint32_t *attr_count, _In_ const sai_attribute_t **attr_list,
79+
_In_ sai_bulk_op_error_mode_t mode, _Out_ sai_object_id_t *object_id,
80+
_Out_ sai_status_t *object_statuses)
81+
{
82+
return mock_sai_next_hop_group->create_next_hop_group_members(switch_id, object_count, attr_count, attr_list, mode,
83+
object_id, object_statuses);
84+
}
85+
86+
sai_status_t remove_next_hop_group_members(_In_ uint32_t object_count, _In_ const sai_object_id_t *object_id,
87+
_In_ sai_bulk_op_error_mode_t mode, _Out_ sai_status_t *object_statuses)
88+
{
89+
return mock_sai_next_hop_group->remove_next_hop_group_members(object_count, object_id, mode, object_statuses);
90+
}

0 commit comments

Comments
 (0)