Skip to content

Commit

Permalink
Checkbox 417/wireless detect (#507)
Browse files Browse the repository at this point in the history
* add test_network_device_info.py for unit test

* modify get_ipv6_address function

* fix import issue

* fix line too long (89 > 79 characters)

* modify space

* update network_device_info.py

move dbus import to where it belong

* Update test_network_device_info.py

modify mocked input

* fix flake8 issue

* remove old comments

* modify the error msg

* modify error msg display
  • Loading branch information
diohe0311 authored Jun 5, 2023
1 parent 4d9cc8c commit 15fc0b7
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 12 deletions.
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***")

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***")

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()

0 comments on commit 15fc0b7

Please sign in to comment.