diff --git a/subiquitycore/models/network.py b/subiquitycore/models/network.py index e81589800..4251ab77d 100644 --- a/subiquitycore/models/network.py +++ b/subiquitycore/models/network.py @@ -209,7 +209,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 @@ -234,10 +239,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() diff --git a/subiquitycore/models/tests/test_network.py b/subiquitycore/models/tests/test_network.py index ce99f64bc..e03333f63 100644 --- a/subiquitycore/models/tests/test_network.py +++ b/subiquitycore/models/tests/test_network.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from unittest.mock import Mock + from subiquitycore.models.network import NetworkDev from subiquitycore.tests import SubiTestCase @@ -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, [])