When a network interface is disconnected from the system (e.g.,
physically removed if it's a USB adapter), probert asynchronously calls
the del_link() method.
Upon receiving this notification, Subiquity server wants to send an
update to the Subiquity clients. The update contains information about
the interface that disappeared - which is obtained through a call to
netdev_info.
Unfortunately, for Wi-Fi and Ethernet interfaces, netdev_info
dereferences the NetworkDev.info variable. Interfaces that no longer
exist on the system (and also interfaces that do not yet exist), have
their "info" variable set to None - so an exception is raised when
dereferencing it.
Wi-Fi interface:
File "subiquitycore/models/network.py", line 227, in netdev_info
scan_state=self.info.wlan['scan_state'],
AttributeError: 'NoneType' object has no attribute 'wlan'
Ethernet interface:
File "subiquitycore/models/network.py", line 201, in netdev_info
is_connected = bool(self.info.is_connected)
AttributeError: 'NoneType' object has no attribute 'is_connected'
Fixed by making sure netdev_info does not raise if the dev.info variable
is None. This is a valid use-case.
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
When accessing the Help menu, Subiquity looks up the IP addresses
currently configured - so it knows whether to show the "Help on SSH
access" option.
Unfortunately, it also looks for IP addresses on devices that were
"configured" through the network screen but that still do not exist in
the system. When such a device exist (e.g., a bond), the Subiquity
client crashes with the following exception:
Traceback (most recent call last):
File "subiquity/common/api/server.py", line 164, in handler
result = await implementation(**args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "subiquity/server/server.py", line 117, in ssh_info_GET
ips.extend(map(str, dev.actual_global_ip_addresses))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "subiquitycore/models/network.py", line 394, in actual_global_ip_addresses
for _, addr in sorted(self.info.addresses.items())
^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'addresses'
A similar crash is observed when calling /network/global_addresses after
creating the bond.
Fixed by only checking the IP addresses of devices that have a
probert.network.Link instance (i.e., they exist in the system).
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
When a Wi-Fi interface is present in the machine configuration (e.g.,
mwhudson.json), the GUI seemingly ignores it. This happens because there
is a filter on the server side which only returns Wi-Fi interfaces if
the wlan_support_install_state() function returns
PackageInstallState.DONE.
However, calling the /network endpoint shows that the state is set to
the wrong value:
{"wlan_support_install_state": "NOT_NEEDED"}
This turns out to be inconsistent because:
* we lean on a PackageInstaller instance to tell if wpasupplicant is
installed (this is what the wlan_support_install_state() function
reflects) ; but
* in dry-run mode, we pretend to install wpasupplicant without
actually relying on the PackageInstaller instance.
Fixed by using the PackageInstaller instance to install the
wpasupplicant package - with a special implementation that only pretends
to install it. This is enough to make the PackageInstaller instance
think the package is installed.
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
When the server raises an exception in a HTTP request handler context,
more often than not, the exception is sent back to the client in the
body.
Additionally, the message of the exception (if any), is also copied as
is in a x-error-msg HTTP header.
That said, HTTP headers must obey strict rules. The "\r\n" sequence
indicate the end of the current HTTP header. When using aiohttp, the
library rejects any header that has a "\r" or "\n" in its value:
ValueError: Newline or carriage return character detected in HTTP status message or header. This is a potential security issue.
As an example, any curtin.util.ProcessExecutionError exception will
contain "\n" characters when converted into a string.
We now encode the error message as JSON before copying it in the HTTP
header.
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
Remove swap space size allocation suggestion. It often won't be used on
smaller installs anyhow.
Drop /boot size to the min instead of max.
Add esp size min into the mix.
(which more than cancels out the /boot change to min)
Reduce padding to max(2G, 50% source min)
When doing an offline install, ubuntu-drivers would sometimes list a
package that is available in the archive but not present in the pool.
This is not something we would expect since we run apt-get update (with
only the pool configured when offline) in the install tree.
However, it turned out that we create the overlay with the lower layers
specified in the wrong order - which essentially makes APT indexes
visible in the source tree also visible in the OEM/third-party driver
overlay.
When calling setup_overlay(lowers=[a, b, c]), Subiquity invokes mount
with lowerdir=c🅱️a (in the reverse order).
This means that c is top, b is middle and a is bottom.
For the OEM and third-party drivers, we build overlays that are based
on:
* the source tree
* the configured tree
* the install tree
Unfortunately, we were doing the opposite. Fixed by reversing the order of
the lower layers.
Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
Controllers have started, but we have decided that no refresh check
is needed, so no check_task was started (or assigned).
GET /refresh is called, resulting in:
DEBUG subiquity.server.server:446 request to /refresh?wait=true crashed
Traceback (most recent call last):
File "subiquity/server/controllers/refresh.py", line 233, in GET
await self.check_task.wait()
AttributeError: 'NoneType' object has no attribute 'wait'
Detecting the bootloader is an obvious choice for real installs, but
is a source of glitches in CI. Default to UEFI, and if tests want
something else they should pass a specific --bootloader.