prober: move to asyncio for probert storage

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2023-04-21 10:45:26 +02:00
parent 1607a8a95b
commit 60f2b506db
5 changed files with 22 additions and 13 deletions

View File

@ -215,7 +215,7 @@ parts:
source: https://github.com/canonical/probert.git source: https://github.com/canonical/probert.git
source-type: git source-type: git
source-commit: dacf369e3dedc50018e4b3b86d4d919459da3cc6 source-commit: 6c3f5c6b9772ef5eaf95e9be631c7780a280e441
override-build: *pyinstall override-build: *pyinstall

View File

@ -30,7 +30,6 @@ from curtin.storage_config import ptable_uuid_to_flag_entry
import pyudev import pyudev
from subiquitycore.async_helpers import ( from subiquitycore.async_helpers import (
run_in_thread,
schedule_task, schedule_task,
SingleInstanceTask, SingleInstanceTask,
TaskAlreadyRunningError, TaskAlreadyRunningError,
@ -887,8 +886,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
probe_types |= {'os'} probe_types |= {'os'}
fname = 'probe-data.json' fname = 'probe-data.json'
key = "ProbeData" key = "ProbeData"
storage = await run_in_thread( storage = await self.app.prober.get_storage(probe_types)
self.app.prober.get_storage, probe_types)
# It is possible for the user to submit filesystem config # It is possible for the user to submit filesystem config
# while a probert probe is running. We don't want to overwrite # while a probert probe is running. We don't want to overwrite
# the users config with a blank one if this happens! (See # the users config with a blank one if this happens! (See

View File

@ -76,6 +76,7 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
self.app.report_start_event = mock.Mock() self.app.report_start_event = mock.Mock()
self.app.report_finish_event = mock.Mock() self.app.report_finish_event = mock.Mock()
self.app.prober = mock.Mock() self.app.prober = mock.Mock()
self.app.prober.get_storage = mock.AsyncMock()
self.app.block_log_dir = '/inexistent' self.app.block_log_dir = '/inexistent'
self.app.note_file_for_apport = mock.Mock() self.app.note_file_for_apport = mock.Mock()
self.fsc = FilesystemController(app=self.app) self.fsc = FilesystemController(app=self.app)
@ -113,7 +114,7 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
self.fsc._configured = False self.fsc._configured = False
self.fsc.locked_probe_data = True self.fsc.locked_probe_data = True
self.fsc.queued_probe_data = None self.fsc.queued_probe_data = None
self.app.prober.get_storage = mock.Mock(return_value={}) self.app.prober.get_storage = mock.AsyncMock(return_value={})
with mock.patch.object(self.fsc.model, 'load_probe_data') as load: with mock.patch.object(self.fsc.model, 'load_probe_data') as load:
await self.fsc._probe_once(restricted=True) await self.fsc._probe_once(restricted=True)
self.assertEqual(self.fsc.queued_probe_data, {}) self.assertEqual(self.fsc.queued_probe_data, {})
@ -125,7 +126,7 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
self.fsc._configured = False self.fsc._configured = False
self.fsc.locked_probe_data = False self.fsc.locked_probe_data = False
self.fsc.queued_probe_data = None self.fsc.queued_probe_data = None
self.app.prober.get_storage = mock.Mock(return_value={}) self.app.prober.get_storage = mock.AsyncMock(return_value={})
with mock.patch.object(self.fsc.model, 'load_probe_data') as load: with mock.patch.object(self.fsc.model, 'load_probe_data') as load:
await self.fsc._probe_once(restricted=True) await self.fsc._probe_once(restricted=True)
self.assertIsNone(self.fsc.queued_probe_data, {}) self.assertIsNone(self.fsc.queued_probe_data, {})
@ -800,6 +801,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
self.app.report_start_event = mock.Mock() self.app.report_start_event = mock.Mock()
self.app.report_finish_event = mock.Mock() self.app.report_finish_event = mock.Mock()
self.app.prober = mock.Mock() self.app.prober = mock.Mock()
self.app.prober.get_storage = mock.AsyncMock()
self.app.snapdapi = snapdapi.make_api_client( self.app.snapdapi = snapdapi.make_api_client(
AsyncSnapd(get_fake_connection())) AsyncSnapd(get_fake_connection()))
self.app.dr_cfg = DRConfig() self.app.dr_cfg = DRConfig()

View File

@ -13,10 +13,12 @@
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import asyncio
import logging import logging
import time
import yaml import yaml
from subiquitycore.async_helpers import run_in_thread
from probert.network import ( from probert.network import (
StoredDataObserver, StoredDataObserver,
UdevObserver, UdevObserver,
@ -41,13 +43,13 @@ class Prober():
observer = UdevObserver(receiver) observer = UdevObserver(receiver)
return observer, observer.start() return observer, observer.start()
def get_storage(self, probe_types=None): async def get_storage(self, probe_types=None):
if self.saved_config is not None: if self.saved_config is not None:
flag = 'bpfail-full' flag = 'bpfail-full'
if probe_types is not None: if probe_types is not None:
flag = 'bpfail-restricted' flag = 'bpfail-restricted'
if flag in self.debug_flags: if flag in self.debug_flags:
time.sleep(2) await asyncio.sleep(2)
1/0 1/0
r = self.saved_config['storage'].copy() r = self.saved_config['storage'].copy()
if probe_types is not None and 'defaults' not in probe_types: if probe_types is not None and 'defaults' not in probe_types:
@ -55,5 +57,12 @@ class Prober():
if k not in probe_types: if k not in probe_types:
r[k] = {} r[k] = {}
return r return r
from probert.storage import Storage from probert.storage import Storage
return Storage().probe(probe_types=probe_types)
# Until probert is completely free of blocking IO, we should continue
# running it in a separate thread.
def run_probert(probe_types):
return asyncio.run(Storage().probe(probe_types=probe_types))
return await run_in_thread(run_probert, probe_types)

View File

@ -19,9 +19,9 @@ from subiquitycore.prober import Prober
class TestProber(SubiTestCase): class TestProber(SubiTestCase):
def test_none_and_defaults_equal(self): async def test_none_and_defaults_equal(self):
with open('examples/simple.json', 'r') as fp: with open('examples/simple.json', 'r') as fp:
prober = Prober(machine_config=fp, debug_flags=()) prober = Prober(machine_config=fp, debug_flags=())
none_storage = prober.get_storage(probe_types=None) none_storage = await prober.get_storage(probe_types=None)
defaults_storage = prober.get_storage(probe_types={'defaults'}) defaults_storage = await prober.get_storage(probe_types={'defaults'})
self.assertEqual(defaults_storage, none_storage) self.assertEqual(defaults_storage, none_storage)