From 2a76b0a8e24d291b2331a4dd05e70a80ef57426b Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Thu, 18 Jul 2019 10:56:50 -0300 Subject: [PATCH 1/6] topotest: fix Lou's framework command wait Fix two main issues: * Don't use float to figure out if we spent the time user asked; * Don't depend on system clock to find we reached the end of time; The fix is basically pre caculating the amount of wait cycles we are going to peform and use a counter. Signed-off-by: Rafael Zalamena --- tests/topotests/lib/lutil.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index d3a275d118..bd26dec67e 100755 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -22,6 +22,7 @@ import sys import time import datetime import json +import math from topolog import logger from mininet.net import Mininet @@ -248,14 +249,18 @@ Total %-4d %-4d %d\n\ found = False n = 0 startt = time.time() - delta = time.time() - startt - while delta < wait and found is False: + + # Calculate the amount of `sleep`s we are going to peform. + wait_count = int(math.ceil(wait / 0.5)) + + while wait_count > 0 and found is False: found = self.command(target, command, regexp, op, result, returnJson) + wait_count -= 1 n+=1 - delta = time.time() - startt - self.log('\tFound: %s n: %s delta: %s and wait: %s' % (found, n, delta, wait)) - if delta < wait and found is False: + if wait_count > 0 and found is False: time.sleep (0.5) + + delta = time.time() - startt self.log('Done after %d loops, time=%s, Found=%s' % (n, delta, found)) found = self.command(target, command, regexp, 'pass', '%s +%4.2f secs' % (result, delta), returnJson) return found From 0f94985fddb1d6776cb1d140ed0cae402ede5250 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Thu, 18 Jul 2019 11:29:33 -0300 Subject: [PATCH 2/6] topotest: adjust luCommand to accept wait_time Allow users of `luCommand` to specify wait time between commands execution and simplify the `wait` method implementation. Signed-off-by: Rafael Zalamena --- tests/topotests/lib/lutil.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index bd26dec67e..7c89ada013 100755 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -243,22 +243,25 @@ Total %-4d %-4d %d\n\ return js return ret - def wait(self, target, command, regexp, op, result, wait, returnJson): - self.log('%s:%s WAIT:%s:%s:%s:%s:%s:%s:' % \ - (self.l_filename, self.l_line, target, command, regexp, op, result,wait)) + def wait(self, target, command, regexp, op, result, wait, returnJson, wait_time=0.5): + self.log('%s:%s WAIT:%s:%s:%s:%s:%s:%s:%s:' % \ + (self.l_filename, self.l_line, target, command, regexp, op, result,wait,wait_time)) found = False n = 0 startt = time.time() # Calculate the amount of `sleep`s we are going to peform. - wait_count = int(math.ceil(wait / 0.5)) + wait_count = int(math.ceil(wait / wait_time)) + 1 - while wait_count > 0 and found is False: + while wait_count > 0: + n += 1 found = self.command(target, command, regexp, op, result, returnJson) + if found is not False: + break + wait_count -= 1 - n+=1 - if wait_count > 0 and found is False: - time.sleep (0.5) + if wait_count > 0: + time.sleep(wait_time) delta = time.time() - startt self.log('Done after %d loops, time=%s, Found=%s' % (n, delta, found)) @@ -286,11 +289,11 @@ def luStart(baseScriptDir='.', baseLogDir='.', net='', LUtil.l_dotall_experiment = False LUtil.l_dotall_experiment = True -def luCommand(target, command, regexp='.', op='none', result='', time=10, returnJson=False): +def luCommand(target, command, regexp='.', op='none', result='', time=10, returnJson=False, wait_time=0.5): if op != 'wait': return LUtil.command(target, command, regexp, op, result, returnJson) else: - return LUtil.wait(target, command, regexp, op, result, time, returnJson) + return LUtil.wait(target, command, regexp, op, result, time, returnJson, wait_time) def luLast(usenl=False): if usenl: From c50435c1c2a207c32e6739bdb02cfc53dff7c5f3 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Thu, 18 Jul 2019 11:30:42 -0300 Subject: [PATCH 3/6] topotest/bgp_l3vpn_to_bgp_vrf: stabilize test Increase wait times between commands execution to help avoid bogus test failures. Signed-off-by: Rafael Zalamena --- .../bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py | 6 +++--- .../bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py index db53c1aacd..0279e482ff 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py @@ -14,12 +14,12 @@ if ret != False and found != None: luCommand('ce1', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num)) luCommand('ce2', 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),'.','none','Removing {} routes'.format(num)) for rtr in rtrs: - luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep Display',' 10 route', 'wait', 'BGP routes removed', wait) + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep Display',' 10 route', 'wait', 'BGP routes removed', wait, wait_time=10) luCommand(rtr, 'vtysh -c "show bgp ipv4 uni"','.', 'none', 'BGP routes post remove') for rtr in rtrs: - luCommand(rtr, 'ip route show | grep -c \\^10\\.','^0$', 'wait', 'Linux routes removed', wait) + luCommand(rtr, 'ip route show | grep -c \\^10\\.','^0$', 'wait', 'Linux routes removed', wait, wait_time=10) luCommand(rtr, 'ip route show','.', 'none', 'Linux routes post remove') rtrs = ['r1', 'r3', 'r4'] for rtr in rtrs: - luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr),'^0$','wait','VRF route removed',wait) + luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr),'^0$','wait','VRF route removed',wait, wait_time=10) #done diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py index 3619cdf84f..21543ab78e 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -36,10 +36,10 @@ if doSharp == True: luCommand('ce2', 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),'','pass','Adding {} routes'.format(num)) rtrs = ['ce1', 'ce2', 'ce3'] for rtr in rtrs: - luCommand(rtr, 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b,c,d), 'Last update:', 'wait', 'RXed last route, 10.{}.{}.{}'.format(b,c,d), wait) - luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', str(num), 'wait', 'See all sharp routes in BGP', wait) - luCommand('r1', 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','RXed -> 10.{}.{}.{} from CE1'.format(b,c,d), wait) - luCommand('r3', 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','RXed -> 10.{}.{}.{} from CE2'.format(b,c,d), wait) + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b,c,d), 'Last update:', 'wait', 'RXed last route, 10.{}.{}.{}'.format(b,c,d), wait, wait_time=10) + luCommand(rtr, 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32', str(num), 'wait', 'See all sharp routes in BGP', wait, wait_time=10) + luCommand('r1', 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','RXed -> 10.{}.{}.{} from CE1'.format(b,c,d), wait, wait_time=10) + luCommand('r3', 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','RXed -> 10.{}.{}.{} from CE2'.format(b,c,d), wait, wait_time=10) luCommand('r1', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d)) luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'99.0.0.2','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d)) luCommand('r3', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'1.1.1.1','wait','see VPN safi -> 10.{}.{}.{} from CE1'.format(b,c,d)) @@ -48,13 +48,13 @@ if doSharp == True: luCommand('r4', 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b,c,d),'3.3.3.3','wait','see VPN safi -> 10.{}.{}.{} from CE2'.format(b,c,d)) rtrs = ['ce1', 'ce2', 'ce3'] for rtr in rtrs: - luCommand(rtr, 'ip route get 10.{}.{}.{}'.format(b,c,d),'dev','wait','Route to 10.{}.{}.{} available'.format(b,c,d), wait) - luCommand(rtr, 'ip route show | grep -c \\^10\\.', str(num), 'wait', 'See {} linux routes'.format(num), wait) + luCommand(rtr, 'ip route get 10.{}.{}.{}'.format(b,c,d),'dev','wait','Route to 10.{}.{}.{} available'.format(b,c,d), wait, wait_time=10) + luCommand(rtr, 'ip route show | grep -c \\^10\\.', str(num), 'wait', 'See {} linux routes'.format(num), wait, wait_time=10) rtrs = ['r1', 'r3', 'r4'] for rtr in rtrs: - luCommand(rtr, 'ip route get vrf {}-cust1 10.{}.{}.{}'.format(rtr,b,c,d),'dev','wait','VRF route available',wait) - luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr), str(num), 'wait','See {} linux routes'.format(num), wait) + luCommand(rtr, 'ip route get vrf {}-cust1 10.{}.{}.{}'.format(rtr,b,c,d),'dev','wait','VRF route available',wait, wait_time=10) + luCommand(rtr, 'ip route show vrf {}-cust1 | grep -c \\^10\\.'.format(rtr), str(num), 'wait','See {} linux routes'.format(num), wait, wait_time=10) rtrs = ['ce1', 'ce2', 'ce3', 'r1', 'r2', 'r3', 'r4'] for rtr in rtrs: ret = luCommand(rtr, 'vtysh -c "show memory"', 'zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)', 'none', 'collect bgpd memory stats') From 3857e52f1205a880e05209cc99752a5bdb2e8a57 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Wed, 17 Jul 2019 18:39:03 -0300 Subject: [PATCH 4/6] topotest: improve reliability of `verify_rib` Attempt to run the function multiple times to make sure we got the result we expected. This is a middle ground between big sleeps and short test intervals. Signed-off-by: Rafael Zalamena --- tests/topotests/lib/common_config.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 628c198e1a..a2ad307f8b 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -30,6 +30,9 @@ import traceback import socket import ipaddr +from lib import topotest + +from functools import partial from lib.topolog import logger, logger_config from lib.topogen import TopoRouter @@ -1089,7 +1092,7 @@ def create_route_maps(tgen, input_dict, build=False): ############################################# # Verification APIs ############################################# -def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): +def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): """ Data will be read from input_dict or input JSON file, API will generate same prefixes, which were redistributed by either create_static_routes() or @@ -1258,6 +1261,19 @@ def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): return True +def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): + "Wrapper function for `_verify_rib` that tries multiple time to get results." + + # Use currying to hide the parameters and create a test function. + test_func = partial(_verify_rib, tgen, addr_type, dut, input_dict, next_hop, protocol) + + # Call the test function and expect it to return True, otherwise try it again. + _, result = topotest.run_and_expect(test_func, True, count=20, wait=6) + + # Return as normal. + return result + + def verify_admin_distance_for_static_routes(tgen, input_dict): """ API to verify admin distance for static routes as defined in input_dict/ From a6fd124a2a4181c48ac6c1ceb2aef70871e348b5 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 22 Jul 2019 13:12:08 -0300 Subject: [PATCH 5/6] topotest: add new run_and_expect variant The new `run_and_expect` variant - called `run_and_expect_type` - tests the return value type of the test function and optionally the return value. Now we can implement tests from test functions that return different return types. Signed-off-by: Rafael Zalamena --- .../topotests/lib/test/test_run_and_expect.py | 74 +++++++++++++++++++ tests/topotests/lib/topotest.py | 48 ++++++++++++ 2 files changed, 122 insertions(+) create mode 100755 tests/topotests/lib/test/test_run_and_expect.py diff --git a/tests/topotests/lib/test/test_run_and_expect.py b/tests/topotests/lib/test/test_run_and_expect.py new file mode 100755 index 0000000000..3c22c20e7b --- /dev/null +++ b/tests/topotests/lib/test/test_run_and_expect.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +# +# test_run_and_expect.py +# Tests for library function: run_and_expect(_type)(). +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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. +# + +""" +Tests for the `run_and_expect(_type)()` functions. +""" + +import os +import sys +import pytest + +# Save the Current Working Directory to find lib files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../../')) + +# pylint: disable=C0413 +from lib.topotest import run_and_expect_type + +def test_run_and_expect_type(): + "Test basic `run_and_expect_type` functionality." + + def return_true(): + "Test function that returns `True`." + return True + + # Test value success. + success, value = run_and_expect_type(return_true, bool, count=1, wait=0, avalue=True) + assert success is True + assert value is True + + # Test value failure. + success, value = run_and_expect_type(return_true, bool, count=1, wait=0, avalue=False) + assert success is False + assert value is True + + # Test type success. + success, value = run_and_expect_type(return_true, bool, count=1, wait=0) + assert success is True + assert value is True + + # Test type failure. + success, value = run_and_expect_type(return_true, str, count=1, wait=0) + assert success is False + assert value is True + + # Test type failure, return correct type. + success, value = run_and_expect_type(return_true, str, count=1, wait=0, avalue=True) + assert success is False + assert value is True + + +if __name__ == '__main__': + sys.exit(pytest.main()) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 867f9f2f03..9e1d344687 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -252,6 +252,54 @@ def run_and_expect(func, what, count=20, wait=3): return (False, result) +def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): + """ + Run `func` and compare the result with `etype`. Do it for `count` times + waiting `wait` seconds between tries. By default it tries 20 times with + 3 seconds delay between tries. + + This function is used when you want to test the return type and, + optionally, the return value. + + Returns (True, func-return) on success or + (False, func-return) on failure. + """ + start_time = time.time() + func_name = "" + if func.__class__ == functools.partial: + func_name = func.func.__name__ + else: + func_name = func.__name__ + + logger.info( + "'{}' polling started (interval {} secs, maximum wait {} secs)".format( + func_name, wait, int(wait * count))) + + while count > 0: + result = func() + if not isinstance(result, etype): + logger.debug("Expected result type '{}' got '{}' instead".format(etype, type(result))) + time.sleep(wait) + count -= 1 + continue + + if etype != type(None) and avalue != None and result != avalue: + logger.debug("Expected value '{}' got '{}' instead".format(avalue, result)) + time.sleep(wait) + count -= 1 + continue + + end_time = time.time() + logger.info("'{}' succeeded after {:.2f} seconds".format( + func_name, end_time - start_time)) + return (True, result) + + end_time = time.time() + logger.error("'{}' failed after {:.2f} seconds".format( + func_name, end_time - start_time)) + return (False, result) + + def int2dpid(dpid): "Converting Integer to DPID" From 91c939dc12593de59316eb998301ec66f1b2eaa3 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Mon, 22 Jul 2019 13:16:35 -0300 Subject: [PATCH 6/6] topotest: improve common_config.py Don't wait for `True` results when the return type is a string. Signed-off-by: Rafael Zalamena --- .../bgp-prefix-list-topo1/test_prefix_lists.py | 16 ++++++++-------- tests/topotests/lib/common_config.py | 13 ++++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py index 25a346f20d..d3892e9d07 100755 --- a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py +++ b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py @@ -385,7 +385,7 @@ def test_ip_prefix_lists_out_permit(request): assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name) @@ -496,7 +496,7 @@ def test_ip_prefix_lists_in_deny_and_permit_any(request): # Verifying RIB routes dut = "r3" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -713,7 +713,7 @@ def test_ip_prefix_lists_out_deny_and_permit_any(request): # Verifying RIB routes dut = "r4" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -858,7 +858,7 @@ def test_modify_prefix_lists_in_permit_to_deny(request): # Verifying RIB routes dut = "r3" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -971,7 +971,7 @@ def test_modify_prefix_lists_in_deny_to_permit(request): # Verifying RIB routes dut = "r3" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -1151,7 +1151,7 @@ def test_modify_prefix_lists_out_permit_to_deny(request): # Verifying RIB routes dut = "r4" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -1264,7 +1264,7 @@ def test_modify_prefix_lists_out_deny_to_permit(request): # Verifying RIB routes dut = "r4" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) @@ -1438,7 +1438,7 @@ def test_ip_prefix_lists_implicit_deny(request): # Verifying RIB routes dut = "r4" protocol = "bgp" - result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol) + result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index a2ad307f8b..75880cfd28 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -1261,14 +1261,21 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): return True -def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): - "Wrapper function for `_verify_rib` that tries multiple time to get results." +def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None, expected=True): + """ + Wrapper function for `_verify_rib` that tries multiple time to get results. + + When the expected result is `False` we actually should expect for an string instead. + """ # Use currying to hide the parameters and create a test function. test_func = partial(_verify_rib, tgen, addr_type, dut, input_dict, next_hop, protocol) # Call the test function and expect it to return True, otherwise try it again. - _, result = topotest.run_and_expect(test_func, True, count=20, wait=6) + if expected is True: + _, result = topotest.run_and_expect(test_func, True, count=20, wait=6) + else: + _, result = topotest.run_and_expect_type(test_func, str, count=20, wait=6) # Return as normal. return result