add support for 'dd' image sources
I think only a core dd image source will work for now. Probably.
This commit is contained in:
parent
d77ac16dd0
commit
0395b6e9b0
|
@ -4,7 +4,7 @@
|
||||||
locale_support: none
|
locale_support: none
|
||||||
name:
|
name:
|
||||||
en: Ubuntu Server
|
en: Ubuntu Server
|
||||||
path: ubuntu-core.squashfs
|
path: pc.img
|
||||||
size: 530485248
|
size: 530485248
|
||||||
type: fsimage
|
type: 'dd-raw:file'
|
||||||
variant: core
|
variant: core
|
||||||
|
|
|
@ -351,6 +351,8 @@ class GuidedCapability(enum.Enum):
|
||||||
CORE_BOOT_PREFER_ENCRYPTED = enum.auto()
|
CORE_BOOT_PREFER_ENCRYPTED = enum.auto()
|
||||||
CORE_BOOT_PREFER_UNENCRYPTED = enum.auto()
|
CORE_BOOT_PREFER_UNENCRYPTED = enum.auto()
|
||||||
|
|
||||||
|
DD = enum.auto()
|
||||||
|
|
||||||
def is_lvm(self) -> bool:
|
def is_lvm(self) -> bool:
|
||||||
return self in [GuidedCapability.LVM, GuidedCapability.LVM_LUKS]
|
return self in [GuidedCapability.LVM, GuidedCapability.LVM_LUKS]
|
||||||
|
|
||||||
|
|
|
@ -1259,6 +1259,7 @@ class FilesystemModel(object):
|
||||||
self.bootloader = bootloader
|
self.bootloader = bootloader
|
||||||
self.storage_version = 1
|
self.storage_version = 1
|
||||||
self._probe_data = None
|
self._probe_data = None
|
||||||
|
self.dd_target: Optional[Disk] = None
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
@ -1718,6 +1719,18 @@ class FilesystemModel(object):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def render(self, mode: ActionRenderMode = ActionRenderMode.DEFAULT):
|
def render(self, mode: ActionRenderMode = ActionRenderMode.DEFAULT):
|
||||||
|
if self.dd_target is not None:
|
||||||
|
return {
|
||||||
|
"partitioning_commands": {
|
||||||
|
"builtin": [
|
||||||
|
"curtin",
|
||||||
|
"block-meta",
|
||||||
|
"simple",
|
||||||
|
"--devices",
|
||||||
|
self.dd_target.path,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
config = {
|
config = {
|
||||||
"storage": {
|
"storage": {
|
||||||
"version": self.storage_version,
|
"version": self.storage_version,
|
||||||
|
|
|
@ -186,6 +186,19 @@ class VariationInfo:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def dd(cls, name: str, min_size: int):
|
||||||
|
return cls(
|
||||||
|
name=name,
|
||||||
|
label=None,
|
||||||
|
min_size=min_size,
|
||||||
|
capability_info=CapabilityInfo(
|
||||||
|
allowed=[
|
||||||
|
GuidedCapability.DD,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FilesystemController(SubiquityController, FilesystemManipulator):
|
class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
endpoint = API.storage
|
endpoint = API.storage
|
||||||
|
@ -363,6 +376,11 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
info = self.info_for_system(name, label, system)
|
info = self.info_for_system(name, label, system)
|
||||||
if info is not None:
|
if info is not None:
|
||||||
self._variation_info[name] = info
|
self._variation_info[name] = info
|
||||||
|
elif catalog_entry.type.startswith("dd-"):
|
||||||
|
min_size = variation.size
|
||||||
|
self._variation_info[name] = VariationInfo.dd(
|
||||||
|
name=name, min_size=min_size
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self._variation_info[name] = VariationInfo.classic(
|
self._variation_info[name] = VariationInfo.classic(
|
||||||
name=name, min_size=variation.size
|
name=name, min_size=variation.size
|
||||||
|
@ -418,6 +436,9 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
spec = dict(fstype="ext4", mount="/")
|
spec = dict(fstype="ext4", mount="/")
|
||||||
self.create_partition(device=gap.device, gap=gap, spec=spec)
|
self.create_partition(device=gap.device, gap=gap, spec=spec)
|
||||||
|
|
||||||
|
def guided_dd(self, disk: ModelDisk):
|
||||||
|
self.model.dd_target = disk
|
||||||
|
|
||||||
def guided_lvm(self, gap, choice: GuidedChoiceV2):
|
def guided_lvm(self, gap, choice: GuidedChoiceV2):
|
||||||
device = gap.device
|
device = gap.device
|
||||||
part_align = device.alignment_data().part_align
|
part_align = device.alignment_data().part_align
|
||||||
|
@ -552,6 +573,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
async def guided(
|
async def guided(
|
||||||
self, choice: GuidedChoiceV2, reset_partition_only: bool = False
|
self, choice: GuidedChoiceV2, reset_partition_only: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
|
self.model.dd_target = None
|
||||||
if choice.capability == GuidedCapability.MANUAL:
|
if choice.capability == GuidedCapability.MANUAL:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -602,6 +624,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
self.guided_zfs(gap, choice)
|
self.guided_zfs(gap, choice)
|
||||||
elif choice.capability == GuidedCapability.DIRECT:
|
elif choice.capability == GuidedCapability.DIRECT:
|
||||||
self.guided_direct(gap)
|
self.guided_direct(gap)
|
||||||
|
elif choice.capability == GuidedCapability.DD:
|
||||||
|
self.guided_dd(disk)
|
||||||
else:
|
else:
|
||||||
raise ValueError("cannot process capability")
|
raise ValueError("cannot process capability")
|
||||||
|
|
||||||
|
@ -1218,6 +1242,9 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
capability = GuidedCapability.LVM_LUKS
|
capability = GuidedCapability.LVM_LUKS
|
||||||
else:
|
else:
|
||||||
capability = GuidedCapability.LVM
|
capability = GuidedCapability.LVM
|
||||||
|
elif name == "dd":
|
||||||
|
capability = GuidedCapability.DD
|
||||||
|
assert mode == "reformat_disk"
|
||||||
elif name == "zfs":
|
elif name == "zfs":
|
||||||
capability = GuidedCapability.ZFS
|
capability = GuidedCapability.ZFS
|
||||||
else:
|
else:
|
||||||
|
@ -1274,6 +1301,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
)
|
)
|
||||||
await self.run_autoinstall_guided(self.ai_data["layout"])
|
await self.run_autoinstall_guided(self.ai_data["layout"])
|
||||||
elif "config" in self.ai_data:
|
elif "config" in self.ai_data:
|
||||||
|
if self.app.base_model.source.current.type.startswith("dd-"):
|
||||||
|
raise Exception("must not use config: when installing a disk image")
|
||||||
# XXX in principle should there be a way to influence the
|
# XXX in principle should there be a way to influence the
|
||||||
# variation chosen here? Not with current use cases for
|
# variation chosen here? Not with current use cases for
|
||||||
# variations anyway.
|
# variations anyway.
|
||||||
|
@ -1342,10 +1371,20 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
self.ensure_probing()
|
self.ensure_probing()
|
||||||
|
|
||||||
def make_autoinstall(self):
|
def make_autoinstall(self):
|
||||||
rendered = self.model.render()
|
if self.model.dd_target is None:
|
||||||
r = {"config": rendered["storage"]["config"]}
|
rendered = self.model.render()
|
||||||
if "swap" in rendered:
|
r = {"config": rendered["storage"]["config"]}
|
||||||
r["swap"] = rendered["swap"]
|
if "swap" in rendered:
|
||||||
|
r["swap"] = rendered["swap"]
|
||||||
|
else:
|
||||||
|
r = {
|
||||||
|
"layout": {
|
||||||
|
"name": "dd",
|
||||||
|
"match": {
|
||||||
|
"path": self.model.dd_target.path,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
|
|
||||||
async def _pre_shutdown(self):
|
async def _pre_shutdown(self):
|
||||||
|
|
|
@ -125,7 +125,7 @@ class InstallController(SubiquityController):
|
||||||
"""Return configuration to be used as part of a curtin 'block-meta'
|
"""Return configuration to be used as part of a curtin 'block-meta'
|
||||||
step."""
|
step."""
|
||||||
cfg = self.model.filesystem.render(mode=mode)
|
cfg = self.model.filesystem.render(mode=mode)
|
||||||
if device_map_path is not None:
|
if "storage" in cfg and device_map_path is not None:
|
||||||
cfg["storage"]["device_map_path"] = str(device_map_path)
|
cfg["storage"]["device_map_path"] = str(device_map_path)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
@ -318,6 +318,7 @@ class InstallController(SubiquityController):
|
||||||
step_config=self.filesystem_config(
|
step_config=self.filesystem_config(
|
||||||
device_map_path=logs_dir / "device-map.json",
|
device_map_path=logs_dir / "device-map.json",
|
||||||
),
|
),
|
||||||
|
source=source,
|
||||||
)
|
)
|
||||||
await run_curtin_step(
|
await run_curtin_step(
|
||||||
name="extract",
|
name="extract",
|
||||||
|
|
|
@ -134,7 +134,7 @@ class SourceController(SubiquityController):
|
||||||
handler = get_handler_for_source(
|
handler = get_handler_for_source(
|
||||||
sanitize_source(self.model.get_source(variation_name))
|
sanitize_source(self.model.get_source(variation_name))
|
||||||
)
|
)
|
||||||
if self.app.opts.dry_run:
|
if handler is not None and self.app.opts.dry_run:
|
||||||
handler = TrivialSourceHandler("/")
|
handler = TrivialSourceHandler("/")
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
|
|
@ -361,6 +361,7 @@ class TestGuided(IsolatedAsyncioTestCase):
|
||||||
self.controller.supports_resilient_boot = True
|
self.controller.supports_resilient_boot = True
|
||||||
self.controller._examine_systems_task.start_sync()
|
self.controller._examine_systems_task.start_sync()
|
||||||
self.app.dr_cfg = DRConfig()
|
self.app.dr_cfg = DRConfig()
|
||||||
|
self.app.base_model.source.current.type = "fsimage"
|
||||||
self.app.base_model.source.current.variations = {
|
self.app.base_model.source.current.variations = {
|
||||||
"default": CatalogEntryVariation(path="", size=1),
|
"default": CatalogEntryVariation(path="", size=1),
|
||||||
}
|
}
|
||||||
|
@ -627,6 +628,7 @@ class TestGuidedV2(IsolatedAsyncioTestCase):
|
||||||
self.fsc.model = self.model = make_model(bootloader)
|
self.fsc.model = self.model = make_model(bootloader)
|
||||||
self.fsc._examine_systems_task.start_sync()
|
self.fsc._examine_systems_task.start_sync()
|
||||||
self.app.dr_cfg = DRConfig()
|
self.app.dr_cfg = DRConfig()
|
||||||
|
self.app.base_model.source.current.type = "fsimage"
|
||||||
self.app.base_model.source.current.variations = {
|
self.app.base_model.source.current.variations = {
|
||||||
"default": CatalogEntryVariation(path="", size=1),
|
"default": CatalogEntryVariation(path="", size=1),
|
||||||
}
|
}
|
||||||
|
@ -1245,6 +1247,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
|
||||||
# runs much more quickly than the integration test!
|
# runs much more quickly than the integration test!
|
||||||
self.fsc.model = model = make_model(Bootloader.UEFI)
|
self.fsc.model = model = make_model(Bootloader.UEFI)
|
||||||
disk = make_disk(model)
|
disk = make_disk(model)
|
||||||
|
self.app.base_model.source.current.type = "fsimage"
|
||||||
self.app.base_model.source.current.variations = {
|
self.app.base_model.source.current.variations = {
|
||||||
"default": CatalogEntryVariation(
|
"default": CatalogEntryVariation(
|
||||||
path="", size=1, snapd_system_label="prefer-encrypted"
|
path="", size=1, snapd_system_label="prefer-encrypted"
|
||||||
|
@ -1338,6 +1341,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
|
||||||
async def test_from_sample_data_defective(self):
|
async def test_from_sample_data_defective(self):
|
||||||
self.fsc.model = model = make_model(Bootloader.UEFI)
|
self.fsc.model = model = make_model(Bootloader.UEFI)
|
||||||
make_disk(model)
|
make_disk(model)
|
||||||
|
self.app.base_model.source.current.type = "fsimage"
|
||||||
self.app.base_model.source.current.variations = {
|
self.app.base_model.source.current.variations = {
|
||||||
"default": CatalogEntryVariation(
|
"default": CatalogEntryVariation(
|
||||||
path="", size=1, snapd_system_label="defective"
|
path="", size=1, snapd_system_label="defective"
|
||||||
|
|
|
@ -148,6 +148,7 @@ class _DryRunCurtinCommand(_CurtinCommand):
|
||||||
|
|
||||||
def make_command(self, command, *args, config=None):
|
def make_command(self, command, *args, config=None):
|
||||||
if command == "install":
|
if command == "install":
|
||||||
|
log.debug("creating substitute for curtin install %s", args)
|
||||||
# Lookup the log file from the config if specified
|
# Lookup the log file from the config if specified
|
||||||
try:
|
try:
|
||||||
with open(config, mode="r") as fh:
|
with open(config, mode="r") as fh:
|
||||||
|
|
|
@ -381,7 +381,10 @@ class GuidedDiskSelectionView(BaseView):
|
||||||
else:
|
else:
|
||||||
capability = GuidedCapability.LVM
|
capability = GuidedCapability.LVM
|
||||||
else:
|
else:
|
||||||
capability = GuidedCapability.DIRECT
|
if GuidedCapability.DD in target.allowed:
|
||||||
|
capability = GuidedCapability.DD
|
||||||
|
else:
|
||||||
|
capability = GuidedCapability.DIRECT
|
||||||
choice = GuidedChoiceV2(
|
choice = GuidedChoiceV2(
|
||||||
target=target,
|
target=target,
|
||||||
capability=capability,
|
capability=capability,
|
||||||
|
|
|
@ -242,7 +242,7 @@ def _compute_widths_for_size(maxcol, table_rows, colspecs, default_spacing):
|
||||||
widths[underlying_i] = max(w, widths.get(underlying_i, 0))
|
widths[underlying_i] = max(w, widths.get(underlying_i, 0))
|
||||||
|
|
||||||
# count the columns...
|
# count the columns...
|
||||||
colcount = max(widths.keys()) // 2 + 1
|
colcount = max(widths.keys(), default=1) // 2 + 1
|
||||||
if unpacked_user_indices:
|
if unpacked_user_indices:
|
||||||
colcount = max(colcount, max(unpacked_user_indices) + 1)
|
colcount = max(colcount, max(unpacked_user_indices) + 1)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue