Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ZTP] Improvements to the connectivity-check plugin #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions src/usr/lib/ztp/plugins/connectivity-check
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -67,24 +67,40 @@ 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
rv = runCommand(pingCmd + host, False)
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
Expand All @@ -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:
Expand All @@ -127,18 +146,24 @@ 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')))

# 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')))
Expand Down
143 changes: 143 additions & 0 deletions tests/test_connectivity-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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):
'''!
Expand All @@ -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:
Expand Down