From d2ab55cc15196d0164103e5c1a339544056d28ca Mon Sep 17 00:00:00 2001
From: Longxiang Lyu <35479537+lolyu@users.noreply.github.com>
Date: Fri, 16 Dec 2022 06:14:46 +0800
Subject: [PATCH] [dualtor] Let T0 delay 10 seconds before sending BGP updates
 (#12996)

Why I did it
To ensure, that after a BGP startup, dualtor T0 receives BGP updates before sending out BGP updates.
Please refer to sonic-net/SONiC#1161 for more details.

How I did it
add coalesce-time 10000 to the frr bgp startup config.

Signed-off-by: Longxiang Lyu <lolv@microsoft.com>
---
 .../docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 |  3 +
 .../sample_output/py2/bgpd_frr_dualtor.conf   | 79 +++++++++++++++++++
 .../sample_output/py3/bgpd_frr_dualtor.conf   | 79 +++++++++++++++++++
 src/sonic-config-engine/tests/test_frr.py     |  8 +-
 4 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 src/sonic-config-engine/tests/sample_output/py2/bgpd_frr_dualtor.conf
 create mode 100644 src/sonic-config-engine/tests/sample_output/py3/bgpd_frr_dualtor.conf

diff --git a/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2
index 4cee01ac973d..15faf4d48332 100644
--- a/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2
+++ b/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2
@@ -68,6 +68,9 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }}
   bgp log-neighbor-changes
   no bgp default ipv4-unicast
   no bgp ebgp-requires-policy
+{% if (DEVICE_METADATA is defined) and ('localhost' in DEVICE_METADATA) and ('subtype' in DEVICE_METADATA['localhost']) and (DEVICE_METADATA['localhost']['subtype'].lower() == 'dualtor') %}
+  coalesce-time 10000
+{% endif %}
 !
 {% if constants.bgp.multipath_relax.enabled is defined and constants.bgp.multipath_relax.enabled %}
   bgp bestpath as-path multipath-relax
diff --git a/src/sonic-config-engine/tests/sample_output/py2/bgpd_frr_dualtor.conf b/src/sonic-config-engine/tests/sample_output/py2/bgpd_frr_dualtor.conf
new file mode 100644
index 000000000000..364a2c34bcaa
--- /dev/null
+++ b/src/sonic-config-engine/tests/sample_output/py2/bgpd_frr_dualtor.conf
@@ -0,0 +1,79 @@
+!
+! template: bgpd/bgpd.conf.j2
+!
+!
+! =========== Managed by sonic-cfggen DO NOT edit manually! ====================
+! generated by templates/quagga/bgpd.conf.j2 with config DB data
+! file: bgpd.conf
+!
+!
+! template: common/daemons.common.conf.j2
+!
+hostname switch-t0
+password zebra
+enable password zebra
+!
+log syslog informational
+log facility local4
+!
+! end of template: common/daemons.common.conf.j2!
+agentx
+!
+!
+!
+! template: bgpd/bgpd.main.conf.j2
+!
+! bgp multiple-instance
+!
+! BGP configuration
+!
+! TSA configuration
+!
+ip prefix-list PL_LoopbackV4 permit 10.1.0.32/32
+!
+ipv6 prefix-list PL_LoopbackV6 permit fc00:1::/64
+!
+ip prefix-list LOCAL_VLAN_IPV4_PREFIX seq 5 permit 192.168.200.0/27
+!
+ip prefix-list LOCAL_VLAN_IPV4_PREFIX seq 10 permit 192.168.0.0/27
+!
+!
+!
+router bgp 65100
+!
+  bgp log-neighbor-changes
+  no bgp default ipv4-unicast
+  no bgp ebgp-requires-policy
+  coalesce-time 10000
+!
+  bgp bestpath as-path multipath-relax
+!
+  bgp graceful-restart restart-time 240
+  bgp graceful-restart
+  bgp graceful-restart preserve-fw-state
+  bgp graceful-restart select-defer-time 45
+!
+  bgp router-id 10.1.0.32
+!
+  network 10.1.0.32/32
+!
+  address-family ipv6
+    network fc00:1::32/64
+  exit-address-family
+!
+  network 192.168.200.1/27
+  network 192.168.0.1/27
+!
+!
+!
+  address-family ipv4
+    maximum-paths 64
+  exit-address-family
+  address-family ipv6
+    maximum-paths 64
+  exit-address-family
+!
+! end of template: bgpd/bgpd.main.conf.j2
+!!
+! end of template: bgpd/bgpd.conf.j2
+!
diff --git a/src/sonic-config-engine/tests/sample_output/py3/bgpd_frr_dualtor.conf b/src/sonic-config-engine/tests/sample_output/py3/bgpd_frr_dualtor.conf
new file mode 100644
index 000000000000..4f606b80838c
--- /dev/null
+++ b/src/sonic-config-engine/tests/sample_output/py3/bgpd_frr_dualtor.conf
@@ -0,0 +1,79 @@
+!
+! template: bgpd/bgpd.conf.j2
+!
+!
+! =========== Managed by sonic-cfggen DO NOT edit manually! ====================
+! generated by templates/quagga/bgpd.conf.j2 with config DB data
+! file: bgpd.conf
+!
+!
+! template: common/daemons.common.conf.j2
+!
+hostname switch-t0
+password zebra
+enable password zebra
+!
+log syslog informational
+log facility local4
+!
+! end of template: common/daemons.common.conf.j2!
+agentx
+!
+!
+!
+! template: bgpd/bgpd.main.conf.j2
+!
+! bgp multiple-instance
+!
+! BGP configuration
+!
+! TSA configuration
+!
+ip prefix-list PL_LoopbackV4 permit 10.1.0.32/32
+!
+ipv6 prefix-list PL_LoopbackV6 permit fc00:1::/64
+!
+ip prefix-list LOCAL_VLAN_IPV4_PREFIX seq 5 permit 192.168.0.0/27
+!
+ip prefix-list LOCAL_VLAN_IPV4_PREFIX seq 10 permit 192.168.200.0/27
+!
+!
+!
+router bgp 65100
+!
+  bgp log-neighbor-changes
+  no bgp default ipv4-unicast
+  no bgp ebgp-requires-policy
+  coalesce-time 10000
+!
+  bgp bestpath as-path multipath-relax
+!
+  bgp graceful-restart restart-time 240
+  bgp graceful-restart
+  bgp graceful-restart preserve-fw-state
+  bgp graceful-restart select-defer-time 45
+!
+  bgp router-id 10.1.0.32
+!
+  network 10.1.0.32/32
+!
+  address-family ipv6
+    network fc00:1::32/64
+  exit-address-family
+!
+  network 192.168.0.1/27
+  network 192.168.200.1/27
+!
+!
+!
+  address-family ipv4
+    maximum-paths 64
+  exit-address-family
+  address-family ipv6
+    maximum-paths 64
+  exit-address-family
+!
+! end of template: bgpd/bgpd.main.conf.j2
+!!
+! end of template: bgpd/bgpd.conf.j2
+!
diff --git a/src/sonic-config-engine/tests/test_frr.py b/src/sonic-config-engine/tests/test_frr.py
index 8f31b4ac7979..3b89f9452c50 100644
--- a/src/sonic-config-engine/tests/test_frr.py
+++ b/src/sonic-config-engine/tests/test_frr.py
@@ -1,4 +1,5 @@
 import filecmp
+import json
 import os
 import subprocess
 
@@ -46,11 +47,13 @@ def run_diff(self, file1, file2):
         _, output = getstatusoutput_noshell(['diff', '-u', file1, file2])
         return output
 
-    def run_case(self, template, target):
+    def run_case(self, template, target, extra_data=None):
         template_dir = os.path.join(self.test_dir, '..', '..', '..', 'dockers', 'docker-fpm-frr', "frr")
         conf_template = os.path.join(template_dir, template)
         constants = os.path.join(self.test_dir, '..', '..', '..', 'files', 'image_config', 'constants', 'constants.yml')
         cmd = ['-m', self.t0_minigraph, '-p', self.t0_port_config, '-y', constants, '-t', conf_template, '-T', template_dir]
+        if extra_data:
+            cmd = ['-a', json.dumps(extra_data)] + cmd
         self.run_script(cmd, output_file=self.output_file)
 
         original_filename = os.path.join(self.test_dir, 'sample_output', utils.PYvX_DIR, target)
@@ -68,3 +71,6 @@ def test_bgpd_frr(self):
     def test_zebra_frr(self):
         self.assertTrue(*self.run_case('zebra/zebra.conf.j2', 'zebra_frr.conf'))
 
+    def test_bgpd_frr_dualtor(self):
+        extra_data = {"DEVICE_METADATA": {"localhost": {"subtype": "DualToR"}}}
+        self.assertTrue(*self.run_case('bgpd/bgpd.conf.j2', 'bgpd_frr_dualtor.conf', extra_data=extra_data))