storage: fix screen sometimes not refreshing after slow probing

When we reach the storage screens on the installer, if the devices
probing operation has not finished, we:
 * display a temporary "Probing" screen.
 * create an asynchronous task (a.k.a., probing task) that will
   eventually show the "Guide Storage" screen when the probing operation
   finishes.

The probing task checks, when it finishes, that the screen currently
visible is the "Probing" screen. This is the expectation and is true in
most scenarios. But in case a different screen is visible, we skip
refreshing the display.

Unfortunately, sometimes, a "Progress" screen is shown for some time
before the "Probing" screen appears. Consequently, we do not refresh the
screen if the probing operation finishes whilst the Progress screen is
visible.

In order to keep the view returned by make_ui() up-to-date and make sure
that the right screen is shown even if the probing operation finishes
early, we use the level indirection that was implemented in make_ui.

https://bugs.launchpad.net/subiquity/+bug/1968161

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-04-12 20:39:22 +02:00
parent 014d9d0d01
commit 414a2235e6
1 changed files with 21 additions and 4 deletions

View File

@ -15,8 +15,10 @@
import asyncio
import logging
from typing import Callable, Optional
from subiquitycore.lsb_release import lsb_release
from subiquitycore.view import BaseView
from subiquity.client.controller import SubiquityTuiController
from subiquity.common.filesystem import gaps
@ -50,19 +52,34 @@ class FilesystemController(SubiquityTuiController, FilesystemManipulator):
self.answers.setdefault('guided', False)
self.answers.setdefault('guided-index', 0)
self.answers.setdefault('manual', [])
self.current_view: Optional[BaseView] = None
async def make_ui(self) -> Callable[[], BaseView]:
def get_current_view() -> BaseView:
assert self.current_view is not None
return self.current_view
async def make_ui(self):
status = await self.endpoint.guided.GET()
if status.status == ProbeStatus.PROBING:
self.app.aio_loop.create_task(self._wait_for_probing())
return SlowProbing(self)
self.current_view = SlowProbing(self)
else:
return self.make_guided_ui(status)
self.current_view = self.make_guided_ui(status)
# NOTE: If we return a BaseView instance directly here, we have no
# guarantee that it will be displayed on the screen by the time the
# probing operation finishes. Therefore, to allow us to reliably
# replace the screen by the "Guided Storage" when the probing operation
# finishes, we add a level of indirection.
# In essence, this allows us to make modifications to the screen
# that eventually will be displayed.
# This is mostly a workaround for the issue described in LP #1968161
return get_current_view
async def _wait_for_probing(self):
status = await self.endpoint.guided.GET(wait=True)
self.current_view = self.make_guided_ui(status)
if isinstance(self.ui.body, SlowProbing):
self.ui.set_body(self.make_guided_ui(status))
self.ui.set_body(self.current_view)
else:
log.debug("not refreshing the display. Current display is %r",
self.ui.body)