call extract_storage_config with probert config to create actions
This commit is contained in:
parent
e6ace190e3
commit
620d7b1973
|
@ -16,8 +16,6 @@
|
||||||
import enum
|
import enum
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from probert.storage import StorageInfo
|
|
||||||
|
|
||||||
from subiquitycore.controller import BaseController
|
from subiquitycore.controller import BaseController
|
||||||
|
|
||||||
from subiquity.models.filesystem import (
|
from subiquity.models.filesystem import (
|
||||||
|
@ -75,11 +73,7 @@ class FilesystemController(BaseController):
|
||||||
5.0, lambda loop, ud: self._check_probe_timeout())
|
5.0, lambda loop, ud: self._check_probe_timeout())
|
||||||
|
|
||||||
def _bg_probe(self, probe_types=None):
|
def _bg_probe(self, probe_types=None):
|
||||||
probed_data = self.prober.get_storage(probe_types=probe_types)
|
return self.prober.get_storage(probe_types=probe_types)
|
||||||
storage = {}
|
|
||||||
for path, data in probed_data["blockdev"].items():
|
|
||||||
storage[path] = StorageInfo({path: data})
|
|
||||||
return storage
|
|
||||||
|
|
||||||
def _probed(self, fut, restricted=False):
|
def _probed(self, fut, restricted=False):
|
||||||
if not restricted and self._probe_state != ProbeState.PROBING:
|
if not restricted and self._probe_state != ProbeState.PROBING:
|
||||||
|
|
|
@ -17,15 +17,17 @@ from abc import ABC, abstractmethod
|
||||||
import attr
|
import attr
|
||||||
import collections
|
import collections
|
||||||
import enum
|
import enum
|
||||||
import glob
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import platform
|
import platform
|
||||||
import sys
|
|
||||||
|
|
||||||
from curtin.util import human2bytes
|
from curtin.util import human2bytes
|
||||||
|
from curtin import storage_config
|
||||||
|
|
||||||
|
from probert.storage import StorageInfo
|
||||||
|
|
||||||
log = logging.getLogger('subiquity.models.filesystem')
|
log = logging.getLogger('subiquity.models.filesystem')
|
||||||
|
|
||||||
|
@ -70,6 +72,9 @@ def _remove_backlinks(obj):
|
||||||
setattr(vv, backlink, None)
|
setattr(vv, backlink, None)
|
||||||
|
|
||||||
|
|
||||||
|
_type_to_cls = {}
|
||||||
|
|
||||||
|
|
||||||
def fsobj(typ):
|
def fsobj(typ):
|
||||||
def wrapper(c):
|
def wrapper(c):
|
||||||
c.__attrs_post_init__ = _set_backlinks
|
c.__attrs_post_init__ = _set_backlinks
|
||||||
|
@ -77,6 +82,7 @@ def fsobj(typ):
|
||||||
c.id = attributes.idfield(typ)
|
c.id = attributes.idfield(typ)
|
||||||
c._m = attr.ib(repr=None, default=None)
|
c._m = attr.ib(repr=None, default=None)
|
||||||
c = attr.s(cmp=False)(c)
|
c = attr.s(cmp=False)(c)
|
||||||
|
_type_to_cls[typ] = c
|
||||||
return c
|
return c
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -554,14 +560,6 @@ class Disk(_Device):
|
||||||
|
|
||||||
_info = attr.ib(default=None)
|
_info = attr.ib(default=None)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_info(self, model, info):
|
|
||||||
d = Disk(m=model, info=info)
|
|
||||||
d.serial = info.serial
|
|
||||||
d.path = info.name
|
|
||||||
d.model = info.model
|
|
||||||
return d
|
|
||||||
|
|
||||||
def info_for_display(self):
|
def info_for_display(self):
|
||||||
bus = self._info.raw.get('ID_BUS', None)
|
bus = self._info.raw.get('ID_BUS', None)
|
||||||
major = self._info.raw.get('MAJOR', None)
|
major = self._info.raw.get('MAJOR', None)
|
||||||
|
@ -1003,14 +1001,67 @@ class FilesystemModel(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bootloader = self._probe_bootloader()
|
self.bootloader = self._probe_bootloader()
|
||||||
self._disk_info = []
|
self._probe_data = None
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._actions = [
|
if self._probe_data is not None:
|
||||||
Disk.from_info(self, info) for info in self._disk_info]
|
config = storage_config.extract_storage_config(self._probe_data)
|
||||||
|
self._actions = self._actions_from_config(
|
||||||
|
config["storage"]["config"],
|
||||||
|
self._probe_data['blockdev'])
|
||||||
|
else:
|
||||||
|
self._actions = []
|
||||||
self.grub_install_device = None
|
self.grub_install_device = None
|
||||||
|
|
||||||
|
def _actions_from_config(self, config, blockdevs):
|
||||||
|
byid = {}
|
||||||
|
objs = []
|
||||||
|
exclusions = set()
|
||||||
|
for action in config:
|
||||||
|
if action['type'] == 'mount':
|
||||||
|
exclusions.add(byid[action['device']])
|
||||||
|
continue
|
||||||
|
c = _type_to_cls.get(action['type'], None)
|
||||||
|
if c is None:
|
||||||
|
# Ignore any action we do not know how to process yet
|
||||||
|
# (e.g. bcache)
|
||||||
|
continue
|
||||||
|
kw = {}
|
||||||
|
for f in attr.fields(c):
|
||||||
|
n = f.name
|
||||||
|
if n not in action:
|
||||||
|
continue
|
||||||
|
v = action[n]
|
||||||
|
if f.metadata.get('ref', False):
|
||||||
|
kw[n] = byid[v]
|
||||||
|
elif f.metadata.get('reflist', False):
|
||||||
|
kw[n] = [byid[id] for id in v]
|
||||||
|
else:
|
||||||
|
kw[n] = v
|
||||||
|
if kw['type'] == 'disk':
|
||||||
|
path = kw['path']
|
||||||
|
kw['info'] = StorageInfo({path: blockdevs[path]})
|
||||||
|
kw['preserve'] = True
|
||||||
|
obj = byid[action['id']] = c(m=self, **kw)
|
||||||
|
objs.append(obj)
|
||||||
|
|
||||||
|
# We filter out anything that can be reached from a currently
|
||||||
|
# mounted device. The motivation here is only to exclude the
|
||||||
|
# media subiquity is mounted from, so this might be a bit
|
||||||
|
# excessive but hey it works.
|
||||||
|
while True:
|
||||||
|
log.debug("exclusions %s", {e.id for e in exclusions})
|
||||||
|
next_exclusions = exclusions.copy()
|
||||||
|
for e in exclusions:
|
||||||
|
next_exclusions.update(itertools.chain(
|
||||||
|
dependencies(e), reverse_dependencies(e)))
|
||||||
|
if len(exclusions) == len(next_exclusions):
|
||||||
|
break
|
||||||
|
exclusions = next_exclusions
|
||||||
|
|
||||||
|
return [o for o in objs if o not in exclusions]
|
||||||
|
|
||||||
def _render_actions(self):
|
def _render_actions(self):
|
||||||
# The curtin storage config has the constraint that an action must be
|
# The curtin storage config has the constraint that an action must be
|
||||||
# preceded by all the things that it depends on. We handle this by
|
# preceded by all the things that it depends on. We handle this by
|
||||||
|
@ -1086,45 +1137,9 @@ class FilesystemModel(object):
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def _get_system_mounted_disks(self):
|
def load_probe_data(self, probe_data):
|
||||||
# This assumes a fairly vanilla setup. It won't list as
|
self._probe_data = probe_data
|
||||||
# mounted a disk that is only mounted via lvm, for example.
|
self.reset()
|
||||||
mounted_devs = []
|
|
||||||
with open('/proc/mounts', encoding=sys.getfilesystemencoding()) as pm:
|
|
||||||
for line in pm:
|
|
||||||
if line.startswith('/dev/'):
|
|
||||||
mounted_devs.append(line.split()[0][5:])
|
|
||||||
mounted_disks = set()
|
|
||||||
for dev in mounted_devs:
|
|
||||||
if os.path.exists('/sys/block/{}'.format(dev)):
|
|
||||||
mounted_disks.add('/dev/' + dev)
|
|
||||||
else:
|
|
||||||
paths = glob.glob('/sys/block/*/{}/partition'.format(dev))
|
|
||||||
if len(paths) == 1:
|
|
||||||
mounted_disks.add('/dev/' + paths[0].split('/')[3])
|
|
||||||
return mounted_disks
|
|
||||||
|
|
||||||
def load_probe_data(self, storage):
|
|
||||||
currently_mounted = self._get_system_mounted_disks()
|
|
||||||
for path, info in storage.items():
|
|
||||||
log.debug("fs probe %s", path)
|
|
||||||
if path in currently_mounted:
|
|
||||||
continue
|
|
||||||
if info.type == 'disk':
|
|
||||||
if info.is_virtual:
|
|
||||||
continue
|
|
||||||
if info.raw["MAJOR"] in ("2", "11"): # serial and cd devices
|
|
||||||
continue
|
|
||||||
if info.raw['attrs'].get('ro') == "1":
|
|
||||||
continue
|
|
||||||
if "ID_CDROM" in info.raw:
|
|
||||||
continue
|
|
||||||
# log.debug('disk={}\n{}'.format(
|
|
||||||
# path, json.dumps(data, indent=4, sort_keys=True)))
|
|
||||||
if info.size < self.lower_size_limit:
|
|
||||||
continue
|
|
||||||
self._disk_info.append(info)
|
|
||||||
self._actions.append(Disk.from_info(self, info))
|
|
||||||
|
|
||||||
def disk_by_path(self, path):
|
def disk_by_path(self, path):
|
||||||
for a in self._actions:
|
for a in self._actions:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import urwid
|
import urwid
|
||||||
|
|
||||||
|
@ -12,14 +11,12 @@ from subiquity.models.filesystem import (
|
||||||
Disk,
|
Disk,
|
||||||
FilesystemModel,
|
FilesystemModel,
|
||||||
)
|
)
|
||||||
|
from subiquity.models.tests.test_filesystem import (
|
||||||
|
FakeStorageInfo,
|
||||||
|
)
|
||||||
from subiquity.ui.views.filesystem.filesystem import FilesystemView
|
from subiquity.ui.views.filesystem.filesystem import FilesystemView
|
||||||
|
|
||||||
|
|
||||||
FakeStorageInfo = namedtuple(
|
|
||||||
'FakeStorageInfo', ['name', 'size', 'free', 'serial', 'model'])
|
|
||||||
FakeStorageInfo.__new__.__defaults__ = (None,) * len(FakeStorageInfo._fields)
|
|
||||||
|
|
||||||
|
|
||||||
class FilesystemViewTests(unittest.TestCase):
|
class FilesystemViewTests(unittest.TestCase):
|
||||||
|
|
||||||
def make_view(self, model, devices=[]):
|
def make_view(self, model, devices=[]):
|
||||||
|
@ -35,9 +32,9 @@ class FilesystemViewTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_one_disk(self):
|
def test_one_disk(self):
|
||||||
model = mock.create_autospec(spec=FilesystemModel)
|
model = mock.create_autospec(spec=FilesystemModel)
|
||||||
disk = Disk.from_info(model, FakeStorageInfo(
|
disk = Disk(
|
||||||
name='disk-name', size=100*(2**20), free=50*(2**20),
|
m=model, serial="DISK-SERIAL",
|
||||||
serial="DISK-SERIAL"))
|
info=FakeStorageInfo(size=100*(2**20), free=50*(2**20)))
|
||||||
view = self.make_view(model, [disk])
|
view = self.make_view(model, [disk])
|
||||||
w = view_helpers.find_with_pred(
|
w = view_helpers.find_with_pred(
|
||||||
view,
|
view,
|
||||||
|
|
Loading…
Reference in New Issue