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
|
||||
name:
|
||||
en: Ubuntu Server
|
||||
path: ubuntu-core.squashfs
|
||||
path: pc.img
|
||||
size: 530485248
|
||||
type: fsimage
|
||||
type: 'dd-raw:file'
|
||||
variant: core
|
||||
|
|
|
@ -351,6 +351,8 @@ class GuidedCapability(enum.Enum):
|
|||
CORE_BOOT_PREFER_ENCRYPTED = enum.auto()
|
||||
CORE_BOOT_PREFER_UNENCRYPTED = enum.auto()
|
||||
|
||||
DD = enum.auto()
|
||||
|
||||
def is_lvm(self) -> bool:
|
||||
return self in [GuidedCapability.LVM, GuidedCapability.LVM_LUKS]
|
||||
|
||||
|
|
|
@ -1259,6 +1259,7 @@ class FilesystemModel(object):
|
|||
self.bootloader = bootloader
|
||||
self.storage_version = 1
|
||||
self._probe_data = None
|
||||
self.dd_target: Optional[Disk] = None
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
|
@ -1718,6 +1719,18 @@ class FilesystemModel(object):
|
|||
return r
|
||||
|
||||
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 = {
|
||||
"storage": {
|
||||
"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):
|
||||
endpoint = API.storage
|
||||
|
@ -363,6 +376,11 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
info = self.info_for_system(name, label, system)
|
||||
if info is not None:
|
||||
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:
|
||||
self._variation_info[name] = VariationInfo.classic(
|
||||
name=name, min_size=variation.size
|
||||
|
@ -418,6 +436,9 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
spec = dict(fstype="ext4", mount="/")
|
||||
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):
|
||||
device = gap.device
|
||||
part_align = device.alignment_data().part_align
|
||||
|
@ -552,6 +573,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
async def guided(
|
||||
self, choice: GuidedChoiceV2, reset_partition_only: bool = False
|
||||
) -> None:
|
||||
self.model.dd_target = None
|
||||
if choice.capability == GuidedCapability.MANUAL:
|
||||
return
|
||||
|
||||
|
@ -602,6 +624,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
self.guided_zfs(gap, choice)
|
||||
elif choice.capability == GuidedCapability.DIRECT:
|
||||
self.guided_direct(gap)
|
||||
elif choice.capability == GuidedCapability.DD:
|
||||
self.guided_dd(disk)
|
||||
else:
|
||||
raise ValueError("cannot process capability")
|
||||
|
||||
|
@ -1218,6 +1242,9 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
capability = GuidedCapability.LVM_LUKS
|
||||
else:
|
||||
capability = GuidedCapability.LVM
|
||||
elif name == "dd":
|
||||
capability = GuidedCapability.DD
|
||||
assert mode == "reformat_disk"
|
||||
elif name == "zfs":
|
||||
capability = GuidedCapability.ZFS
|
||||
else:
|
||||
|
@ -1274,6 +1301,8 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
)
|
||||
await self.run_autoinstall_guided(self.ai_data["layout"])
|
||||
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
|
||||
# variation chosen here? Not with current use cases for
|
||||
# variations anyway.
|
||||
|
@ -1342,10 +1371,20 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
self.ensure_probing()
|
||||
|
||||
def make_autoinstall(self):
|
||||
rendered = self.model.render()
|
||||
r = {"config": rendered["storage"]["config"]}
|
||||
if "swap" in rendered:
|
||||
r["swap"] = rendered["swap"]
|
||||
if self.model.dd_target is None:
|
||||
rendered = self.model.render()
|
||||
r = {"config": rendered["storage"]["config"]}
|
||||
if "swap" in rendered:
|
||||
r["swap"] = rendered["swap"]
|
||||
else:
|
||||
r = {
|
||||
"layout": {
|
||||
"name": "dd",
|
||||
"match": {
|
||||
"path": self.model.dd_target.path,
|
||||
},
|
||||
},
|
||||
}
|
||||
return r
|
||||
|
||||
async def _pre_shutdown(self):
|
||||
|
|
|
@ -125,7 +125,7 @@ class InstallController(SubiquityController):
|
|||
"""Return configuration to be used as part of a curtin 'block-meta'
|
||||
step."""
|
||||
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)
|
||||
return cfg
|
||||
|
||||
|
@ -318,6 +318,7 @@ class InstallController(SubiquityController):
|
|||
step_config=self.filesystem_config(
|
||||
device_map_path=logs_dir / "device-map.json",
|
||||
),
|
||||
source=source,
|
||||
)
|
||||
await run_curtin_step(
|
||||
name="extract",
|
||||
|
|
|
@ -134,7 +134,7 @@ class SourceController(SubiquityController):
|
|||
handler = get_handler_for_source(
|
||||
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("/")
|
||||
return handler
|
||||
|
||||
|
|
|
@ -361,6 +361,7 @@ class TestGuided(IsolatedAsyncioTestCase):
|
|||
self.controller.supports_resilient_boot = True
|
||||
self.controller._examine_systems_task.start_sync()
|
||||
self.app.dr_cfg = DRConfig()
|
||||
self.app.base_model.source.current.type = "fsimage"
|
||||
self.app.base_model.source.current.variations = {
|
||||
"default": CatalogEntryVariation(path="", size=1),
|
||||
}
|
||||
|
@ -627,6 +628,7 @@ class TestGuidedV2(IsolatedAsyncioTestCase):
|
|||
self.fsc.model = self.model = make_model(bootloader)
|
||||
self.fsc._examine_systems_task.start_sync()
|
||||
self.app.dr_cfg = DRConfig()
|
||||
self.app.base_model.source.current.type = "fsimage"
|
||||
self.app.base_model.source.current.variations = {
|
||||
"default": CatalogEntryVariation(path="", size=1),
|
||||
}
|
||||
|
@ -1245,6 +1247,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
|
|||
# runs much more quickly than the integration test!
|
||||
self.fsc.model = model = make_model(Bootloader.UEFI)
|
||||
disk = make_disk(model)
|
||||
self.app.base_model.source.current.type = "fsimage"
|
||||
self.app.base_model.source.current.variations = {
|
||||
"default": CatalogEntryVariation(
|
||||
path="", size=1, snapd_system_label="prefer-encrypted"
|
||||
|
@ -1338,6 +1341,7 @@ class TestCoreBootInstallMethods(IsolatedAsyncioTestCase):
|
|||
async def test_from_sample_data_defective(self):
|
||||
self.fsc.model = model = make_model(Bootloader.UEFI)
|
||||
make_disk(model)
|
||||
self.app.base_model.source.current.type = "fsimage"
|
||||
self.app.base_model.source.current.variations = {
|
||||
"default": CatalogEntryVariation(
|
||||
path="", size=1, snapd_system_label="defective"
|
||||
|
|
|
@ -148,6 +148,7 @@ class _DryRunCurtinCommand(_CurtinCommand):
|
|||
|
||||
def make_command(self, command, *args, config=None):
|
||||
if command == "install":
|
||||
log.debug("creating substitute for curtin install %s", args)
|
||||
# Lookup the log file from the config if specified
|
||||
try:
|
||||
with open(config, mode="r") as fh:
|
||||
|
|
|
@ -381,7 +381,10 @@ class GuidedDiskSelectionView(BaseView):
|
|||
else:
|
||||
capability = GuidedCapability.LVM
|
||||
else:
|
||||
capability = GuidedCapability.DIRECT
|
||||
if GuidedCapability.DD in target.allowed:
|
||||
capability = GuidedCapability.DD
|
||||
else:
|
||||
capability = GuidedCapability.DIRECT
|
||||
choice = GuidedChoiceV2(
|
||||
target=target,
|
||||
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))
|
||||
|
||||
# count the columns...
|
||||
colcount = max(widths.keys()) // 2 + 1
|
||||
colcount = max(widths.keys(), default=1) // 2 + 1
|
||||
if unpacked_user_indices:
|
||||
colcount = max(colcount, max(unpacked_user_indices) + 1)
|
||||
|
||||
|
|
Loading…
Reference in New Issue