From 1a402dd93974840e1039972276e8dbf1cf8694fe Mon Sep 17 00:00:00 2001 From: Viacheslav Hletenko Date: Thu, 6 Apr 2023 12:24:52 +0000 Subject: T1237: Failover route add checks for multiple targets There is only one target for checking ICMP/ARP Extend it for checking multiple targets set protocols failover route 192.0.2.55/32 next-hop 192.168.122.1 check target '203.0.113.1' set protocols failover route 192.0.2.55/32 next-hop 192.168.122.1 check target '203.0.113.11' The route will be installed only if all targets are 'alive' --- interface-definitions/protocols-failover.xml.in | 1 + src/helpers/vyos-failover.py | 69 ++++++++++++++++--------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/interface-definitions/protocols-failover.xml.in b/interface-definitions/protocols-failover.xml.in index 900c76eab..a8c5c717f 100644 --- a/interface-definitions/protocols-failover.xml.in +++ b/interface-definitions/protocols-failover.xml.in @@ -48,6 +48,7 @@ + diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py index 0de945f20..03fb42f57 100755 --- a/src/helpers/vyos-failover.py +++ b/src/helpers/vyos-failover.py @@ -30,7 +30,7 @@ my_name = Path(__file__).stem def is_route_exists(route, gateway, interface, metric): """Check if route with expected gateway, dev and metric exists""" - rc, data = rc_cmd(f'sudo ip --json route show protocol failover {route} ' + rc, data = rc_cmd(f'ip --json route show protocol failover {route} ' f'via {gateway} dev {interface} metric {metric}') if rc == 0: data = json.loads(data) @@ -72,6 +72,7 @@ def get_best_route_options(route, debug=False): f'best_metric: {best_metric}, best_iface: {best_interface}') return best_gateway, best_interface, best_metric + def is_port_open(ip, port): """ Check connection to remote host and port @@ -91,32 +92,54 @@ def is_port_open(ip, port): finally: s.close() -def is_target_alive(target=None, iface='', proto='icmp', port=None, debug=False): - """ - Host availability check by ICMP, ARP, TCP - Return True if target checks is successful - % is_target_alive('192.0.2.1', 'eth1', proto='arp') - True +def is_target_alive(target_list=None, iface='', proto='icmp', port=None, debug=False): + """Check the availability of each target in the target_list using + the specified protocol ICMP, ARP, TCP + + Args: + target_list (list): A list of IP addresses or hostnames to check. + iface (str): The name of the network interface to use for the check. + proto (str): The protocol to use for the check. Options are 'icmp', 'arp', or 'tcp'. + port (int): The port number to use for the TCP check. Only applicable if proto is 'tcp'. + debug (bool): If True, print debug information during the check. + + Returns: + bool: True if all targets are reachable, False otherwise. + + Example: + % is_target_alive(['192.0.2.1', '192.0.2.5'], 'eth1', proto='arp') + True """ if iface != '': iface = f'-I {iface}' - if proto == 'icmp': - command = f'/usr/bin/ping -q {target} {iface} -n -c 2 -W 1' - rc, response = rc_cmd(command) - if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc == 0: - return True - elif proto == 'arp': - command = f'/usr/bin/arping -b -c 2 -f -w 1 -i 1 {iface} {target}' - rc, response = rc_cmd(command) - if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc == 0: - return True - elif proto == 'tcp' and port is not None: - return True if is_port_open(target, port) else False - else: - return False + + for target in target_list: + match proto: + case 'icmp': + command = f'/usr/bin/ping -q {target} {iface} -n -c 2 -W 1' + rc, response = rc_cmd(command) + if debug: + print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') + if rc != 0: + return False + + case 'arp': + command = f'/usr/bin/arping -b -c 2 -f -w 1 -i 1 {iface} {target}' + rc, response = rc_cmd(command) + if debug: + print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') + if rc != 0: + return False + + case _ if proto == 'tcp' and port is not None: + if not is_port_open(target, port): + return False + + case _: + return False + + return True if __name__ == '__main__': -- cgit v1.2.3