Skip to content

Commit d54fc38

Browse files
author
Rodny Molina
committed
Routing-stack warm-reboot feature.
Please refer to the corresponding design document for more details: sonic-net/SONiC#239 The following manual UT plan has been successfully executed. UT automation pending. Physical topology formed by various BGP peers connecting to the DUT. Both non-ecmp and ecmp prefixes are learned/tested. Testcase Suite 1: Making use of “pkill -9 bgpd && pkill -9 zebra” as trigger. ============= IPv4 prefixes ========== * Restart zebra/bgpd: - Verify that all prefixes are properly stale-marked and that no change is pushed to AppDB during reconciliation. - Result: PASSED * Restart zebra/bgpd and add one new non-ecmp IPv4 prefix. - To produce a route-state-inconsistency, add prefix in adjacent node before bgp sessions are re-established. - Verify that new-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and withdraw one non-ecmp IPv4 prefix. - To produce a route-state-inconsistency, remove prefix in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and add one new path to an IPv4 ecmp-prefix. - To produce a route-state-inconsistency, add prefix-path in adjacent node before bgp sessions are re-established. - Verify that new prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and withdraw one ecmp-path from an existing ecmp IPv4 prefix. - To produce a route-state-inconsistency, remove prefix-path in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Eventually, during reconciliation, this path will be eliminated as it’s not being refreshed by remote-peer. - Result: PASSED IPv6 prefixes ========== * Restart zebra/bgpd: - Verify that all prefixes are properly stale-marked and that no change is pushed to AppDB during reconciliation. - Result: PASSED * Restart zebra/bgpd and add one new non-ecmp IPv6 prefix. - To produce a route-state-inconsistency, add prefix in adjacent node before bgp sessions are re-established. - Verify that new-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and withdraw one non-ecmp IPv6 prefix. - To produce a route-state-inconsistency, remove prefix in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and add one new path to an IPv6 ecmp-prefix. - To produce a route-state-inconsistency, add prefix-path in adjacent node before bgp sessions are re-established. - Verify that new prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart zebra/bgpd and withdraw one ecmp-path from an existing ecmp IPv6 prefix. - To produce a route-state-inconsistency, remove prefix-path in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Eventually, during reconciliation, this path will be eliminated as it’s not being refreshed by remote-peer. - Result: PASSED Testcase Suite 2: Making use of sudo service bgp restart” as trigger. ============= IPv4 prefixes ========== * Restart bgp service/docker: - Verify that all prefixes are properly stale-marked and that no change is pushed to AppDB during reconciliation. - Result: PASSED * Restart bgp service/docker and add one new non-ecmp IPv4 prefix. - To produce a route-state-inconsistency, add prefix in adjacent node before bgp sessions are re-established. - Verify that new-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and withdraw one non-ecmp IPv4 prefix. - To produce a route-state-inconsistency, remove prefix in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and add one new path to an IPv4 ecmp-prefix. - To produce a route-state-inconsistency, add prefix-path in adjacent node before bgp sessions are re-established. - Verify that new prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and withdraw one ecmp-path from an existing ecmp IPv4 prefix. - To produce a route-state-inconsistency, remove prefix-path in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Eventually, during reconciliation, this path will be eliminated as it’s not being refreshed by remote-peer. - Result: PASSED IPv6 prefixes ========== * Restart bgp service/docker: - Verify that all prefixes are properly stale-marked and that no change is pushed to AppDB during reconciliation. - Result: PASSED * Restart bgp service/docker and add one new non-ecmp IPv6 prefix. - To produce a route-state-inconsistency, add prefix in adjacent node before bgp sessions are re-established. - Verify that new-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and withdraw one non-ecmp IPv6 prefix. - To produce a route-state-inconsistency, remove prefix in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and add one new path to an IPv6 ecmp-prefix. - To produce a route-state-inconsistency, add prefix-path in adjacent node before bgp sessions are re-established. - Verify that new prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Result: PASSED * Restart bgp service/docker and withdraw one ecmp-path from an existing ecmp IPv6 prefix. - To produce a route-state-inconsistency, remove prefix-path in adjacent node before bgp sessions are re-established. - Verify that deleted-prefix-path msg is NOT pushed down to AppDB till reconciliation takes place. - Eventually, during reconciliation, this path will be eliminated as it’s not being refreshed by remote-peer. - Result: PASSED RB= G=lnos-reviewers R=pchaudhary,pmao,rmolina,samaity,sfardeen,zxu A= Signed-off-by: Rodny Molina <[email protected]>
1 parent 7c90bb3 commit d54fc38

File tree

6 files changed

+716
-16
lines changed

6 files changed

+716
-16
lines changed

fpmsyncd/Makefile.am

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
INCLUDES = -I $(top_srcdir) -I $(FPM_PATH)
1+
INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I $(FPM_PATH)
22

33
bin_PROGRAMS = fpmsyncd
44

@@ -8,9 +8,8 @@ else
88
DBGFLAGS = -g
99
endif
1010

11-
fpmsyncd_SOURCES = fpmsyncd.cpp fpmlink.cpp routesync.cpp
11+
fpmsyncd_SOURCES = fpmsyncd.cpp fpmlink.cpp routesync.cpp $(top_srcdir)/warmrestart/warmRestartHelper.cpp $(top_srcdir)/warmrestart/warmRestartHelper.h $(top_srcdir)/warmrestart/warm_restart.cpp $(top_srcdir)/warmrestart/warm_restart.h
1212

1313
fpmsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON)
1414
fpmsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON)
1515
fpmsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon
16-

fpmsyncd/fpmsyncd.cpp

+61-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
#include <iostream>
22
#include "logger.h"
33
#include "select.h"
4+
#include "selectabletimer.h"
45
#include "netdispatcher.h"
6+
#include "warmRestartHelper.h"
57
#include "fpmsyncd/fpmlink.h"
68
#include "fpmsyncd/routesync.h"
79

10+
811
using namespace std;
912
using namespace swss;
1013

14+
15+
/*
16+
* Default warm-restart timer interval for routing-stack app. To be used only if
17+
* no explicit value has been defined in configuration.
18+
*/
19+
const uint32_t DEFAULT_ROUTING_RESTART_INTERVAL = 120;
20+
21+
1122
int main(int argc, char **argv)
1223
{
1324
swss::Logger::linkToDbNative("fpmsyncd");
@@ -18,25 +29,70 @@ int main(int argc, char **argv)
1829
NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, &sync);
1930
NetDispatcher::getInstance().registerMessageHandler(RTM_DELROUTE, &sync);
2031

21-
while (1)
32+
while (true)
2233
{
2334
try
2435
{
2536
FpmLink fpm;
2637
Select s;
2738

28-
cout << "Waiting for connection..." << endl;
39+
SelectableTimer warmStartTimer(timespec{0, 0});
40+
41+
cout << "Waiting for fpm-client connection..." << endl;
2942
fpm.accept();
3043
cout << "Connected!" << endl;
3144

3245
s.addSelectable(&fpm);
46+
47+
/* Initialize warm-restart logic if this one is enabled */
48+
bool warmStartEnabled = sync.m_warmStartHelper.isEnabled();
49+
if (warmStartEnabled)
50+
{
51+
/* Obtain warm-restart timer defined for routing application */
52+
uint32_t warmRestartIval = sync.m_warmStartHelper.getRestartTimer();
53+
if (!warmRestartIval)
54+
{
55+
warmStartTimer.setInterval(timespec{DEFAULT_ROUTING_RESTART_INTERVAL, 0});
56+
}
57+
else
58+
{
59+
warmStartTimer.setInterval(timespec{warmRestartIval, 0});
60+
}
61+
62+
/* Execute recovery instruction and kick off warm-restart timer */
63+
if (sync.m_warmStartHelper.runRecovery())
64+
{
65+
warmStartTimer.start();
66+
s.addSelectable(&warmStartTimer);
67+
}
68+
}
69+
3370
while (true)
3471
{
3572
Selectable *temps;
36-
/* Reading FPM messages forever (and calling "readData" to read them) */
73+
74+
/* Reading FPM messages forever (and calling "readMe" to read them) */
3775
s.select(&temps);
38-
pipeline.flush();
39-
SWSS_LOG_DEBUG("Pipeline flushed");
76+
77+
/*
78+
* Upon expiration of the warm-restart timer, proceed to run the
79+
* reconciliation process and remove warm-restart timer from
80+
* select() loop.
81+
*/
82+
if (warmStartEnabled && temps == &warmStartTimer)
83+
{
84+
SWSS_LOG_NOTICE("Warm-Restart timer expired.");
85+
sync.m_warmStartHelper.reconciliate();
86+
s.removeSelectable(&warmStartTimer);
87+
88+
pipeline.flush();
89+
SWSS_LOG_NOTICE("Pipeline flushed");
90+
}
91+
else if (!warmStartEnabled || sync.m_warmStartHelper.isReconciled())
92+
{
93+
pipeline.flush();
94+
SWSS_LOG_NOTICE("Pipeline flushed");
95+
}
4096
}
4197
}
4298
catch (FpmLink::FpmConnectionClosedException &e)

fpmsyncd/routesync.cpp

+46-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ using namespace std;
1414
using namespace swss;
1515

1616
RouteSync::RouteSync(RedisPipeline *pipeline) :
17-
m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true)
17+
m_routeTable(pipeline, APP_ROUTE_TABLE_NAME, true),
18+
m_warmStartHelper(pipeline, &m_routeTable, "bgp", "bgp")
1819
{
1920
m_nl_sock = nl_socket_alloc();
2021
nl_connect(m_nl_sock, NETLINK_ROUTE);
@@ -38,10 +39,30 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj)
3839
return;
3940
}
4041

42+
/*
43+
* Upon arrival of a delete msg we could either push the change right away,
44+
* or we could opt to defer it if we are in the middle of a warm-reboot
45+
* process. The goal here is to avoid unnecessary churn in swss/syncd layers.
46+
*/
47+
auto warmState = m_warmStartHelper.getState();
48+
4149
if (nlmsg_type == RTM_DELROUTE)
4250
{
43-
m_routeTable.del(destipprefix);
44-
return;
51+
if (warmState == WarmStart::INITIALIZED ||
52+
warmState == WarmStart::RECONCILED)
53+
{
54+
m_routeTable.del(destipprefix);
55+
return;
56+
}
57+
else
58+
{
59+
SWSS_LOG_INFO("Warm-Restart: Receiving delete msg: %s\n", destipprefix);
60+
61+
vector<FieldValueTuple> fvVector;
62+
const KeyOpFieldsValuesTuple kfv = std::make_tuple(destipprefix, "", fvVector);
63+
m_warmStartHelper.removeRecoveryMap(kfv, WarmStartHelper::DELETE);
64+
return;
65+
}
4566
}
4667
else if (nlmsg_type != RTM_NEWROUTE)
4768
{
@@ -118,8 +139,28 @@ void RouteSync::onMsg(int nlmsg_type, struct nl_object *obj)
118139
vector<FieldValueTuple> fvVector;
119140
FieldValueTuple nh("nexthop", nexthops);
120141
FieldValueTuple idx("ifname", ifnames);
142+
121143
fvVector.push_back(nh);
122144
fvVector.push_back(idx);
123-
m_routeTable.set(destipprefix, fvVector);
124-
SWSS_LOG_DEBUG("RoutTable set: %s %s %s\n", destipprefix, nexthops.c_str(), ifnames.c_str());
145+
146+
if (warmState == WarmStart::INITIALIZED ||
147+
warmState == WarmStart::RECONCILED)
148+
{
149+
m_routeTable.set(destipprefix, fvVector);
150+
SWSS_LOG_DEBUG("RouteTable set msg: %s %s %s\n",
151+
destipprefix, nexthops.c_str(), ifnames.c_str());
152+
}
153+
154+
/*
155+
* During routing-stack restarting scenarios route-updates will be temporarily
156+
* put on hold by warm-reboot logic.
157+
*/
158+
else
159+
{
160+
SWSS_LOG_INFO("Warm-Restart: RouteTable set msg: %s %s %s\n",
161+
destipprefix, nexthops.c_str(), ifnames.c_str());
162+
163+
const KeyOpFieldsValuesTuple kfv = std::make_tuple(destipprefix, "", fvVector);
164+
m_warmStartHelper.insertRecoveryMap(kfv, WarmStartHelper::CLEAN);
165+
}
125166
}

fpmsyncd/routesync.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "dbconnector.h"
55
#include "producerstatetable.h"
66
#include "netmsg.h"
7+
#include "warmRestartHelper.h"
8+
79

810
namespace swss {
911

@@ -16,10 +18,12 @@ class RouteSync : public NetMsg
1618

1719
virtual void onMsg(int nlmsg_type, struct nl_object *obj);
1820

21+
WarmStartHelper m_warmStartHelper;
22+
1923
private:
20-
ProducerStateTable m_routeTable;
21-
struct nl_cache *m_link_cache;
22-
struct nl_sock *m_nl_sock;
24+
ProducerStateTable m_routeTable;
25+
struct nl_cache *m_link_cache;
26+
struct nl_sock *m_nl_sock;
2327
};
2428

2529
}

0 commit comments

Comments
 (0)