diff --git a/src/usr/lib/ztp/plugins/connectivity-check b/src/usr/lib/ztp/plugins/connectivity-check index b8d0360..b31414e 100755 --- a/src/usr/lib/ztp/plugins/connectivity-check +++ b/src/usr/lib/ztp/plugins/connectivity-check @@ -39,7 +39,7 @@ class ConnectivityCheck: ''' self.__input_file = input_file - def pingHosts(self, host_list, retry_count, retry_interval, ping_count, deadline, timeout, ipv6=False): + def pingHosts(self, host_list, interface, retry_count, retry_interval, ping_count, deadline, timeout, args, ipv6=False, dhcp=False): # Prepare list of hosts to ping if not isinstance(host_list, list): @@ -67,8 +67,12 @@ class ConnectivityCheck: pingCmd += "-w " + str(deadline) + " " if timeout is not None: pingCmd += "-W " + str(timeout) + " " + if interface is not None: + pingCmd += "-I " + interface + " " if ipv6: - pingCmd = pingCmd + " -6 " + pingCmd = pingCmd + " -6 " + if args is not None: + pingCmd = pingCmd + " " + args + " " logger.info('connectivity-check: Pinging host \'%s\'.' % (host)) updateActivity('connectivity-check: Pinging host \'%s\'.' % (host)) # Ping the host @@ -76,15 +80,27 @@ class ConnectivityCheck: if rv == 0: # Host is alive, remove it from the list _host_list.remove(host) - logger.info('connectivity-check: Host \'%s\' not reachable.' % (host)) - updateActivity('connectivity-check: Host \'%s\' not reachable.' % (host)) + else: + logger.info('connectivity-check: Host \'%s\' not reachable.' % (host)) + updateActivity('connectivity-check: Host \'%s\' not reachable.' % (host)) else: # Discard invalid hosts _host_list.remove(host) if len(_host_list) == 0: break + + if dhcp: + logger.info('Restarting networking service to restart DHCP') + updateActivity('Restarting networking service to restart DHCP') + runCommand('systemctl restart interfaces-config') + logger.info('Restarted networking') + updateActivity('Restarted networking') + + logger.info('connectivity-check: Sleeping for %d seconds before retrying.' % (retry_interval)) + updateActivity('Sleeping for %d seconds before retrying' % (retry_interval)) time.sleep(retry_interval) + if retry_count != -1: retry_count = retry_count - 1 return rc @@ -108,6 +124,9 @@ class ConnectivityCheck: logger.error('connectivity-check: Host list not provided.') sys.exit(1) + # Interface name + interface = getField(section_data, 'interface', str, None) + # Time interval in seconds to wait before retrying ping to hosts retry_interval = getField(section_data, 'retry-interval', int, 5) if retry_interval < 0: @@ -127,10 +146,16 @@ class ConnectivityCheck: # Time to wait for a response, in seconds timeout = getField(section_data, 'timeout', int, None) + # Time to wait for a response, in seconds + retry_dhcp = getField(section_data, 'retry-dhcp', bool, default_value=False) + + # Additional ping arguments + args = getField(section_data, 'args', str, None) + # Ping ipv4 host list if section_data.get('ping-hosts') is not None: logger.info('connectivity-check: Attempting to connect to IPv4 hosts %s.' % (section_data.get('ping-hosts'))) - if self.pingHosts(section_data.get('ping-hosts'), retry_count, retry_interval, ping_count, deadline, timeout) is False: + if self.pingHosts(section_data.get('ping-hosts'), interface, retry_count, retry_interval, ping_count, deadline, timeout, args, dhcp=retry_dhcp) is False: logger.error('connectivity-check: IPv4 hosts not reachable.') sys.exit(1) logger.info('connectivity-check: All IPv4 hosts %s reachable' %(section_data.get('ping-hosts'))) @@ -138,7 +163,7 @@ class ConnectivityCheck: # Ping ipv6 host list if section_data.get('ping6-hosts') is not None: logger.info('connectivity-check: Attempting to connect to IPv6 hosts %s.' % (section_data.get('ping6-hosts'))) - if self.pingHosts(section_data.get('ping6-hosts'), retry_count, retry_interval, ping_count, deadline, timeout, ipv6=True) is False: + if self.pingHosts(section_data.get('ping6-hosts'), interface, retry_count, retry_interval, ping_count, deadline, timeout, args, ipv6=True, dhcp=retry_dhcp) is False: logger.error('connectivity-check: IPv6 hosts not reachable.') sys.exit(1) logger.info('connectivity-check: All IPv6 hosts %s reachable' %(section_data.get('ping6-hosts'))) diff --git a/tests/test_connectivity-check.py b/tests/test_connectivity-check.py index e7b95b8..88bab8f 100644 --- a/tests/test_connectivity-check.py +++ b/tests/test_connectivity-check.py @@ -87,6 +87,98 @@ def test_ping_localhost(self, tmpdir): assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 + def test_ping_localhost_interface_nok(self, tmpdir): + '''! + Test case pinging IPV4 localhost using an interface: + Verify that pinging IPV4 localhost fails on non existent interface + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping-hosts": "127.0.0.1", + "interface": "ethX", + "retry-count": 2, + "retry-interval": 15, + "timeout": "10" + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 1 + + def test_ping_localhost_interface_ok(self, tmpdir): + '''! + Test case pinging IPV4 localhost using an interface: + Verify that pinging IPV4 localhost succeeds on existent interface + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping-hosts": "127.0.0.1", + "interface": "lo", + "deadline": 15 + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 0 + + def test_ping_localhost_args_nok(self, tmpdir): + '''! + Test case pinging IPV4 localhost using arguments: + Verify that pinging IPV4 localhost fails on non existent interface + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping-hosts": "127.0.0.1", + "args": "-I ethX", + "retry-count": 2, + "retry-interval": 15, + "timeout": "10" + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 1 + + def test_ping_localhost_args_ok(self, tmpdir): + '''! + Test case pinging IPV4 localhost using arguments: + Verify that pinging IPV4 localhost succeeds on existent interface + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping-hosts": "127.0.0.1", + "args": "-I lo", + "deadline": 15 + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 0 + def test_ping_non_routable_address(self, tmpdir): '''! Test case pinging non routable IPV4 address: @@ -98,17 +190,21 @@ def test_ping_non_routable_address(self, tmpdir): { "01-connectivity-check": { "retry-count": 2, + "retry-dhcp" : true, "retry-interval": 15, "timeout": "10", "ping-hosts": ["192.0.2.1", 123] } } """) + (rc, interfaces_exit_time, cmd_stderr) = runCommand("systemctl show --value -p ExecMainExitTimestampMonotonic interfaces-config") connectivity_check = ConnectivityCheck(str(fh)) with pytest.raises(SystemExit) as pytest_wrapped_e: connectivity_check.main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 1 + (rc, interfaces_exit_time_new, cmd_stderr) = runCommand("systemctl show --value -p ExecMainExitTimestampMonotonic interfaces-config") + assert (int(interfaces_exit_time_new[0]) > int(interfaces_exit_time[0])) def test_ping_ipv6_localhost(self, tmpdir): '''! @@ -132,6 +228,53 @@ def test_ping_ipv6_localhost(self, tmpdir): assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 0 + def test_ping_ipv6_localhost_interface_nok(self, tmpdir): + '''! + Test case pinging IPV6 localhost using an interface + Verify that pinging IPV6 localhost fails + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping6-hosts": ["0:0:0:0:0:0:0:1"], + "interface": "ethX", + "retry-count": 2, + "retry-interval": 15, + "timeout": "10" + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 1 + + def test_ping_ipv6_localhost_interface_ok(self, tmpdir): + '''! + Test case pinging IPV6 localhost using an interface + Verify that pinging IPV6 localhost succeeds + ''' + d = tmpdir.mkdir("valid") + fh = d.join("input.json") + fh.write(""" + { + "connectivity-check": { + "ping6-hosts": ["0:0:0:0:0:0:0:1"], + "interface": "lo", + "retry-count": -2, + "retry-interval": -15 + } + } + """) + connectivity_check = ConnectivityCheck(str(fh)) + with pytest.raises(SystemExit) as pytest_wrapped_e: + connectivity_check.main() + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 0 + def test_ping_ipv6_non_routable_address(self, tmpdir): '''! Test case pinging non routable IPV6 address: