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

Checkbox 417/wireless detect #507

Merged
merged 11 commits into from
Jun 5, 2023
30 changes: 18 additions & 12 deletions providers/base/bin/network_device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
from subprocess import check_output, CalledProcessError, STDOUT
import sys

import dbus

from checkbox_support.parsers.modinfo import ModinfoParser
from checkbox_support.parsers.udevadm import UdevadmParser

Expand Down Expand Up @@ -82,14 +80,14 @@ def is_iface_connected(cls, iface):
def get_ipv4_address(interface):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
ipv4_addr = socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', interface[:15].encode())
)[20:24])
except Exception as e:
print("ERROR: getting the IPv4 address for %s: %s" %
(interface, repr(e)))

buffer = struct.pack('256s', interface[:15].encode())
# retrieve the IP address associated with a network interface.
data = fcntl.ioctl(s.fileno(), 0x8915, buffer)
ipv4_addr = socket.inet_ntoa(data[20:24])
except OSError as e:
print("OSError: failed to get the IPv4 address for %s: %s" %
(interface, repr(e)), file=sys.stderr)
ipv4_addr = "***NOT CONFIGURED***"
finally:
return ipv4_addr
Expand All @@ -99,7 +97,14 @@ def get_ipv6_address(interface):
cmd = ['/sbin/ip', '-6', '-o', 'addr', 'show', 'dev', interface,
'scope', 'link']
proc = check_output(cmd, universal_newlines=True)
return proc.split()[3].strip()
try:
# If it failed at split(), it means there is no output from cmd.
ipv6_addr = proc.split()[3].strip()
except IndexError as e:
print("IndexError: failed to get the IPv6 address for %s: %s" %
(interface, repr(e)), file=sys.stderr)
ipv6_addr = "***NOT CONFIGURED***"
return ipv6_addr

@classmethod
def get_mac_address(cls, interface):
Expand Down Expand Up @@ -278,7 +283,6 @@ def _driver_populate(self):


class NMDevices():

# This example lists basic information about network interfaces known to NM
devtypes = {1: "Ethernet",
2: "WiFi",
Expand All @@ -296,6 +300,7 @@ def __len__(self):
return len(self._devices)

def _collect_devices(self):
import dbus
bus = dbus.SystemBus()
proxy = bus.get_object("org.freedesktop.NetworkManager",
"/org/freedesktop/NetworkManager")
Expand All @@ -304,6 +309,7 @@ def _collect_devices(self):

def devices(self):
"""Convert to list of NetworkDevice with NM derived attrs set"""
import dbus
for d in self._devices:
bus = dbus.SystemBus()
dev_proxy = bus.get_object("org.freedesktop.NetworkManager", d)
Expand Down
92 changes: 92 additions & 0 deletions providers/base/tests/test_network_device_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env python3
# Copyright 2020 Canonical Ltd.
# Written by:
# Dio He <dio.he@canonical.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest
from io import StringIO
from unittest.mock import Mock, patch
from contextlib import redirect_stderr

from network_device_info import Utils

class GetIpv4AddressTests(unittest.TestCase):
# There are 2 output we could get from fcntl.ioctl():
# 1. No WiFi connected
# (It pulls out bytes data directly from kernel, so if the given SIOCGIFADDR doesn't exist, it raise a OSError)

# 2. Connected with WiFi
# It will pulls out a 256 bytes data into buffer, then we get to parse the IP address
@patch("fcntl.ioctl")
def test_get_ipv4_address_without_connection(self, mock_ioctl):
mock_ioctl.side_effect = OSError()
interface = "wlo1"
with redirect_stderr(StringIO()) as stderr:
addr = Utils.get_ipv4_address(interface)

self.assertEqual(addr, "***NOT CONFIGURED***")
diohe0311 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_ipv4_address_with_connection(self):
test_input = (b'wlo1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x02\x00\x00\x00\xc0\xa8De\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
mock_inet_ntoa = Mock(return_value=test_input)
with patch("fcntl.ioctl", mock_inet_ntoa):
interface = "wlo1"
addr = Utils.get_ipv4_address(interface)
self.assertEqual(addr, "192.168.68.101")

class GetIpv6AddressTests(unittest.TestCase):
# There are 3 kind of output it could return, but only 2 will happen in this case.
# 1. No WiFi connected

# 2. Connected with WiFi

# 3. Connected with WiFi, but we ask the wrong interface name
# (This would not happen in our case, because the name is given by NetworkManager)
def test_get_ipv6_address_without_connection(self):
test_input = ""
mock_check_output = Mock(return_value=test_input)
with patch("network_device_info.check_output", mock_check_output):
interface = "wlo1"
with redirect_stderr(StringIO()) as stderr:
addr = Utils.get_ipv6_address(interface)

self.assertEqual(addr, "***NOT CONFIGURED***")
diohe0311 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_ipv6_address_with_connection(self):
test_input = "2: wlo1 inet6 fe80::d9eb:3f93:c7b2:86ba/64 scope link noprefixroute \ valid_lft forever preferred_lft forever"
mock_check_output = Mock(return_value=test_input)
with patch("network_device_info.check_output", mock_check_output):
interface = "wlo1"
addr = Utils.get_ipv6_address(interface)
self.assertEqual(addr, "fe80::d9eb:3f93:c7b2:86ba/64")

if __name__ == '__main__':
unittest.main()