summaryrefslogtreecommitdiffstats
path: root/tests/topotests/bgp_gshut/test_bgp_gshut.py
blob: 764252d9621e25af902f395f8c0114847dc81eb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
#!/usr/bin/env python

#
# test_bgp_gshut.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2020 by
# Vivek Venkatraman <vivek@nvidia.com>
#
# Permission to use, copy, modify, and/or distribute this software
# for any purpose with or without fee is hereby granted, provided
# that the above copyright notice and this permission notice appear
# in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
#

"""
Test the ability to initiate and stop BGP graceful shutdown.
Test both the vrf-specific and global configuration and operation.

r1
|
r2----r3
| \
|  \
r4  r5


r2 is UUT and peers with r1 and r3 in default bgp instance and
with r4 and r5 in vrf vrf1.
r1-r2 peering is iBGP and the other peerings are eBGP.

Check r2 initial convergence in default table
Define update-delay with max-delay in the default bgp instance on r2
Shutdown peering on r1 toward r2 so that delay timers can be exercised
Clear bgp neighbors on r2 and then check for the 'in progress' indicator
Check that r2 only installs route learned from r4 after the max-delay timer expires
Define update-delay with max-delay and estabish-wait and check json output showing set
Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time
Remove update-delay timer on r2 to verify that it goes back to normal behavior
Clear neighbors on r2 and check that route install time on r2 does not delay
Define global bgp update-delay with max-delay and establish-wait on r2
Check that r2 default instance and vrf1 have the max-delay and establish set
Clear neighbors on r2 and check route-install time is after the establish-wait timer

Note that the keepalive/hold times were changed to 3/9 and the connect retry timer
to 10 to improve the odds the convergence timing in this test case is useful in the
event of packet loss.
"""

import os
import re
import sys
import json
import pytest
import platform
from functools import partial

CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))

# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger

pytestmark = [pytest.mark.bgpd]


def build_topo(tgen):
    for routern in range(1, 6):
        tgen.add_router("r{}".format(routern))

    switch = tgen.add_switch("s1")
    switch.add_link(tgen.gears["r1"])
    switch.add_link(tgen.gears["r2"])

    switch = tgen.add_switch("s2")
    switch.add_link(tgen.gears["r2"])
    switch.add_link(tgen.gears["r3"])

    switch = tgen.add_switch("s3")
    switch.add_link(tgen.gears["r2"])
    switch.add_link(tgen.gears["r4"])

    switch = tgen.add_switch("s4")
    switch.add_link(tgen.gears["r2"])
    switch.add_link(tgen.gears["r5"])


def _run_cmd_and_check(router, cmd, results_file, retries=100, intvl=0.5):
    json_file = "{}/{}".format(CWD, results_file)
    expected = json.loads(open(json_file).read())
    test_func = partial(topotest.router_json_cmp, router, cmd, expected)
    return topotest.run_and_expect(test_func, None, retries, intvl)


def setup_module(mod):
    tgen = Topogen(build_topo, mod.__name__)
    tgen.start_topology()

    router_list = tgen.routers()
    krel = platform.release()
    if topotest.version_cmp(krel, "4.5") < 0:
        tgen.errors = "Linux kernel version of at least 4.5 needed for bgp-gshut tests"
        pytest.skip(tgen.errors)

    # Configure vrf and its slaves in the kernel on r2
    r2 = tgen.gears["r2"]
    r2.run("ip link add vrf1 type vrf table 1000")
    r2.run("ip link set vrf1 up")
    r2.run("ip link set r2-eth2 master vrf1")
    r2.run("ip link set r2-eth3 master vrf1")

    # Load FRR config and initialize all routers
    for i, (rname, router) in enumerate(router_list.items(), 1):
        router.load_config(
            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
        )
        router.load_config(
            TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
        )

    tgen.start_router()

    # Basic peering test to see if things are ok
    _, result = _run_cmd_and_check(r2, "show ip bgp summary json", "r2/bgp_sum_1.json")
    assertmsg = "R2: Basic sanity test after init failed -- global peerings not up"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r2, "show ip bgp vrf vrf1 summary json", "r2/bgp_sum_2.json"
    )
    assertmsg = "R2: Basic sanity test after init failed -- VRF peerings not up"
    assert result is None, assertmsg


def teardown_module(mod):
    tgen = get_topogen()
    tgen.stop_topology()


def test_bgp_gshut():
    tgen = get_topogen()

    if tgen.routers_have_failure():
        pytest.skip(tgen.errors)

    r1 = tgen.gears["r1"]
    r2 = tgen.gears["r2"]
    r3 = tgen.gears["r3"]
    r4 = tgen.gears["r4"]
    r5 = tgen.gears["r5"]

    # Verify initial route states
    logger.info("\nVerify initial route states")

    _, result = _run_cmd_and_check(
        r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
    )
    assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
    )
    assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
    )
    assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    logger.info("\nInitial route states are as expected")

    # "Test #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers"
    logger.info(
        "\nTest #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers"
    )

    r2.vtysh_cmd(
        """
          configure terminal
            bgp graceful-shutdown
        """
    )

    # R1, R3 and R5 should see routes from R2 with GSHUT. In addition,
    # R1 should see LOCAL_PREF of 0
    _, result = _run_cmd_and_check(
        r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_2.json"
    )
    assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_2.json"
    )
    assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json"
    )
    assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    logger.info(
        "\nTest #1: Successful, routes have GSHUT and/or LPREF of 0 as expected"
    )

    # "Test #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers"
    logger.info(
        "\nTest #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers"
    )

    r2.vtysh_cmd(
        """
          configure terminal
            no bgp graceful-shutdown
        """
    )

    # R1, R3 and R5 should see routes from R2 with their original attributes
    _, result = _run_cmd_and_check(
        r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
    )
    assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
    )
    assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
    )
    assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    logger.info(
        "\nTest #2: Successful, routes have their original attributes with default LPREF and without GSHUT"
    )

    # "Test #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers"
    logger.info(
        "\nTest #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers"
    )

    r2.vtysh_cmd(
        """
          configure terminal
            router bgp 65001 vrf vrf1
              bgp graceful-shutdown
        """
    )

    # R1 and R3 should see no change to their routes
    _, result = _run_cmd_and_check(
        r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
    )
    assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
    )
    assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    # R5 should see routes from R2 with GSHUT.
    _, result = _run_cmd_and_check(
        r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json"
    )
    assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    logger.info("\nTest #3: Successful, only VRF peers like R5 see routes with GSHUT")

    # "Test #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1"
    logger.info(
        "\nTest #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1"
    )

    ret = r2.vtysh_cmd(
        """
          configure terminal
            bgp graceful-shutdown
        """
    )

    # This should fail
    assertmsg = "R2: BGP-wide graceful-shutdown config not rejected even though it is enabled in VRF1"
    assert (
        re.search("global graceful-shutdown not permitted", ret) is not None
    ), assertmsg

    logger.info(
        "\nTest #4: Successful, BGP-wide graceful-shutdown rejected as it is enabled in VRF"
    )

    # "Test #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers"
    logger.info(
        "\nTest #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers"
    )

    r2.vtysh_cmd(
        """
          configure terminal
            router bgp 65001 vrf vrf1
              no bgp graceful-shutdown
        """
    )

    # R1 and R3 should see no change to their routes
    _, result = _run_cmd_and_check(
        r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
    )
    assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    _, result = _run_cmd_and_check(
        r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
    )
    assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    # R5 should see routes from R2 with original attributes.
    _, result = _run_cmd_and_check(
        r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
    )
    assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
    assert result is None, assertmsg

    logger.info(
        "\nTest #5: Successful, routes have their original attributes with default LPREF and without GSHUT"
    )

    # tgen.mininet_cli()


if __name__ == "__main__":
    args = ["-s"] + sys.argv[1:]
    sys.exit(pytest.main(args))