Skip to content

Commit 71591b7

Browse files
authored
Merge pull request #4823 from l0crian1/fix-wlb-multi-int
wlb: T7977: Fix weight calculation for multiple interfaces
2 parents c21b066 + 3545176 commit 71591b7

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

python/vyos/utils/misc.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#
1313
# You should have received a copy of the GNU Lesser General Public
1414
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
15+
import time
16+
17+
from typing import Callable, Any
1518

1619
def begin(*args):
1720
"""
@@ -64,3 +67,29 @@ def install_into_config(conf, config_paths, override_prompt=True):
6467

6568
if count > 0:
6669
print(f'{count} value(s) installed. Use "compare" to see the pending changes, and "commit" to apply.')
70+
71+
def wait_for(
72+
func: Callable[..., Any],
73+
*args,
74+
interval: float = 1.0,
75+
timeout: float = 5.0,
76+
**kwargs
77+
) -> bool:
78+
"""
79+
Repeatedly calls `func()` until it returns True or the timeout expires.
80+
81+
Args:
82+
func: A function with no arguments that returns a truthy value when ready.
83+
interval: Seconds to wait between calls (default: 1.0).
84+
timeout: Maximum time to wait in seconds (default: 5.0).
85+
86+
Returns:
87+
True if the function returned True within the timeout, otherwise False.
88+
"""
89+
start = time.monotonic()
90+
while True:
91+
if func(*args, **kwargs):
92+
return True
93+
if (time.monotonic() - start) >= timeout:
94+
return False
95+
time.sleep(interval)

python/vyos/wanloadbalance.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def wlb_weight_interfaces(rule_conf, health_state):
154154
for ifname, weight in sorted(interfaces, key=lambda i: i[1]): # build weight ranges
155155
end = start + weight - 1
156156
out.append((ifname, f'{start}-{end}' if end > start else start))
157-
start = weight
157+
start += weight
158158

159159
return out, total_weight
160160

smoketest/scripts/cli/test_load-balancing_wan.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
from base_vyostest_shim import VyOSUnitTestSHIM
2222
from vyos.utils.file import chmod_755
2323
from vyos.utils.file import write_file
24+
from vyos.utils.misc import wait_for
2425
from vyos.utils.process import call
2526
from vyos.utils.process import cmd
27+
from vyos.utils.process import rc_cmd
2628

2729
base_path = ['load-balancing']
2830

@@ -428,6 +430,77 @@ def test_firewall_groups(self):
428430

429431
self.verify_nftables_chain(nftables_search, 'ip vyos_wanloadbalance', 'wlb_mangle_prerouting')
430432

433+
def test_3_or_more_interfaces_in_rule(self):
434+
lan_iface = 'eth1'
435+
436+
# Interfaces for equal weight test
437+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '101', 'address', '203.0.113.2/30'])
438+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '102', 'address', '203.0.113.6/30'])
439+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '103', 'address', '203.0.113.10/30'])
440+
441+
# Interfaces for unequal weight test
442+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '201', 'address', '203.0.113.14/30'])
443+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '202', 'address', '203.0.113.18/30'])
444+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '203', 'address', '203.0.113.22/30'])
445+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '204', 'address', '203.0.113.26/30'])
446+
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '205', 'address', '203.0.113.30/30'])
447+
448+
449+
self.cli_set(['interfaces', 'ethernet', lan_iface, 'vif', '100', 'address', '198.51.100.2/30'])
450+
self.cli_set(['interfaces', 'ethernet', lan_iface, 'vif', '200', 'address', '198.51.100.6/30'])
451+
452+
# Health checks for equal weight test
453+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.101', 'nexthop', '203.0.113.2'])
454+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.102', 'nexthop', '203.0.113.6'])
455+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.103', 'nexthop', '203.0.113.10'])
456+
self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', f'{lan_iface}.100'])
457+
self.cli_set(base_path + ['wan', 'rule', '10', 'interface', 'eth0.101'])
458+
self.cli_set(base_path + ['wan', 'rule', '10', 'interface', 'eth0.102'])
459+
self.cli_set(base_path + ['wan', 'rule', '10', 'interface', 'eth0.103'])
460+
461+
# Health checks for unequal weight test
462+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.201', 'nexthop', '203.0.113.14'])
463+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.202', 'nexthop', '203.0.113.18'])
464+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.203', 'nexthop', '203.0.113.22'])
465+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.204', 'nexthop', '203.0.113.26'])
466+
self.cli_set(base_path + ['wan', 'interface-health', 'eth0.205', 'nexthop', '203.0.113.30'])
467+
self.cli_set(base_path + ['wan', 'rule', '20', 'inbound-interface', f'{lan_iface}.200'])
468+
self.cli_set(base_path + ['wan', 'rule', '20', 'interface', 'eth0.201', 'weight', '2'])
469+
self.cli_set(base_path + ['wan', 'rule', '20', 'interface', 'eth0.202', 'weight', '4'])
470+
self.cli_set(base_path + ['wan', 'rule', '20', 'interface', 'eth0.203', 'weight', '4'])
471+
self.cli_set(base_path + ['wan', 'rule', '20', 'interface', 'eth0.204', 'weight', '7'])
472+
self.cli_set(base_path + ['wan', 'rule', '20', 'interface', 'eth0.205', 'weight', '4'])
473+
474+
# commit changes
475+
self.cli_commit()
476+
477+
def check_wlb_status():
478+
rc, wlb_status = rc_cmd('nft list chain ip vyos_wanloadbalance wlb_mangle_prerouting')
479+
if rc != 0:
480+
return False
481+
482+
# get all lines containing 'jump'
483+
lines = [l for l in wlb_status.splitlines() if 'jump' in l]
484+
485+
# check total count of 'jump' across all matching lines
486+
total_jumps = sum(l.count('jump') for l in lines)
487+
488+
return total_jumps == 8
489+
490+
wait_for(check_wlb_status)
491+
492+
nftables_search = [
493+
['0 : jump wlb_mangle_isp_eth0.101',
494+
'1 : jump wlb_mangle_isp_eth0.102',
495+
'2 : jump wlb_mangle_isp_eth0.103'],
496+
['0-1 : jump wlb_mangle_isp_eth0.201',
497+
'2-5 : jump wlb_mangle_isp_eth0.202',
498+
'6-9 : jump wlb_mangle_isp_eth0.203',
499+
'10-13 : jump wlb_mangle_isp_eth0.205',
500+
'14-20 : jump wlb_mangle_isp_eth0.204'],
501+
]
502+
503+
self.verify_nftables_chain(nftables_search, 'ip vyos_wanloadbalance', 'wlb_mangle_prerouting')
431504

432505
if __name__ == '__main__':
433506
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())

0 commit comments

Comments
 (0)