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

network: fix crash when Wi-Fi or eth interface gets removed from the system #1789

Merged
merged 1 commit into from Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 15 additions & 3 deletions subiquitycore/models/network.py
Expand Up @@ -205,7 +205,12 @@ def __init__(self, model, name, typ):

def netdev_info(self) -> NetDevInfo:
if self.type == "eth":
is_connected = bool(self.info.is_connected)
if self.info is not None:
is_connected = bool(self.info.is_connected)
else:
# If the device has just disappeared, let's pretend it's not
# connected.
is_connected = False
else:
is_connected = True
bond_master = None
Expand All @@ -230,10 +235,17 @@ def netdev_info(self) -> NetDevInfo:
wlan: Optional[WLANStatus] = None
if self.type == "wlan":
ssid, psk = self.configured_ssid
# If the device has just disappeared, let's pretend it's not
# scanning and has no visible SSID.
scan_state = None
visible_ssids: List[str] = []
if self.info is not None:
scan_state = self.info.wlan["scan_state"]
visible_ssids = self.info.wlan["visible_ssids"]
wlan = WLANStatus(
config=WLANConfig(ssid=ssid, psk=psk),
scan_state=self.info.wlan["scan_state"],
visible_ssids=self.info.wlan["visible_ssids"],
scan_state=scan_state,
visible_ssids=visible_ssids,
)

dhcp_addresses = self.dhcp_addresses()
Expand Down
25 changes: 25 additions & 0 deletions subiquitycore/models/tests/test_network.py
Expand Up @@ -13,6 +13,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from unittest.mock import Mock

from subiquitycore.models.network import NetworkDev
from subiquitycore.tests import SubiTestCase

Expand All @@ -39,3 +41,26 @@ def test_remove_v6(self):
self.nd.remove_routes(6)
expected = self.ipv4s
self.assertEqual(expected, self.nd.config["routes"])


class TestNetworkDev(SubiTestCase):
def test_netdev_info_eth_inexistent(self):
# LP: #2012659 - just after physically removing an Ethernet interface
# from the system, Subiquity tries to collect information via
# netdev_info. The code would try to dereference dev.info - but it was
# reset to None when the interface got removed.
# In other private reports, the same issue would occur with Wi-Fi
# interfaces.
model = Mock(get_all_netdevs=Mock(return_value=[]))
nd = NetworkDev(model, "testdev0", "eth")
info = nd.netdev_info()
self.assertFalse(info.is_connected)

def test_netdev_info_wlan_inexistent(self):
# Just like test_netdev_info_eth_inexistent but with Wi-Fi interfaces
# which suffer the same issue.
model = Mock(get_all_netdevs=Mock(return_value=[]))
nd = NetworkDev(model, "testdev0", "wlan")
info = nd.netdev_info()
self.assertIsNone(info.wlan.scan_state)
self.assertEqual(info.wlan.visible_ssids, [])