assembler/qemu: support generic partition layouts
Introduce two new assembler options `pttype` and `partitions` to allow fine grained control over how the partition table is created. The first one controls the partition type, either `mbr` (default, when the key is missing) or `gpt`; if specified the `partitions` key must contain a list of objects describing the individual partitions (`start`, `size`, `type`) together with a `filesystem` object describing the filesystem (`type`, `uuid`, `mountpoint`) to be created on that partition. In the case the `pttype` option is missing, the legacy mode is used where `root_fs_uuid` and `root_fs_type` need to be specified.
This commit is contained in:
parent
9688859acf
commit
5eb4ceff2f
1 changed files with 102 additions and 7 deletions
|
|
@ -14,11 +14,13 @@ STAGE_DESC = "Assemble a bootable partitioned disk image with qemu-img"
|
||||||
STAGE_INFO = """
|
STAGE_INFO = """
|
||||||
Assemble a bootable partitioned disk image using `qemu-img`.
|
Assemble a bootable partitioned disk image using `qemu-img`.
|
||||||
|
|
||||||
Creates a sparse MBR-partitioned disk image of the given `size`, with a single
|
Creates a sparse partitioned disk image of type `pttype` of a given `size`,
|
||||||
bootable partition containing the root filesystem.
|
with a partition table according to `partitions` or a MBR partitioned disk
|
||||||
|
having a single bootable partition containing the root filesystem if the
|
||||||
|
`pttype` property is absent.
|
||||||
|
|
||||||
Installs GRUB2 (using the buildhost's `/usr/lib/grub/i386-pc/boot.img` etc.) as
|
If the partition type is MBR it installs GRUB2 (using the buildhost's
|
||||||
the bootloader.
|
`/usr/lib/grub/i386-pc/boot.img` etc.) as the bootloader.
|
||||||
|
|
||||||
Copies the tree contents into the root filesystem and then converts the raw
|
Copies the tree contents into the root filesystem and then converts the raw
|
||||||
sparse image into the format requested with the `fmt` option.
|
sparse image into the format requested with the `fmt` option.
|
||||||
|
|
@ -27,7 +29,12 @@ Buildhost commands used: `truncate`, `mount`, `umount`, `sfdisk`,
|
||||||
`grub2-mkimage`, `mkfs.ext4` or `mkfs.xfs`, `qemu-img`.
|
`grub2-mkimage`, `mkfs.ext4` or `mkfs.xfs`, `qemu-img`.
|
||||||
"""
|
"""
|
||||||
STAGE_OPTS = """
|
STAGE_OPTS = """
|
||||||
"required": ["format", "filename", "ptuuid", "root_fs_uuid", "size"],
|
"required": ["format", "filename", "ptuuid", "size"],
|
||||||
|
"oneOf": [{
|
||||||
|
"required": ["root_fs_uuid"]
|
||||||
|
},{
|
||||||
|
"required": ["pttype", "partitions"]
|
||||||
|
}],
|
||||||
"properties": {
|
"properties": {
|
||||||
"format": {
|
"format": {
|
||||||
"description": "Image file format to use",
|
"description": "Image file format to use",
|
||||||
|
|
@ -38,10 +45,61 @@ STAGE_OPTS = """
|
||||||
"description": "Image filename",
|
"description": "Image filename",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"partitions": {
|
||||||
|
"description": "Partition layout ",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"description": "Description of one partition",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bootable": {
|
||||||
|
"description": "Mark the partition as bootable (MBR)",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"description": "The size of this partition",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"description": "The start offset of this partition",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "The partition type (UUID or identifier)",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"filesystem": {
|
||||||
|
"description": "Description of the filesystem",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["mountpoint", "type", "uuid"],
|
||||||
|
"properties": {
|
||||||
|
"mountpoint": {
|
||||||
|
"description": "Where to mount the partition",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "Type of the filesystem",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["ext4", "xfs"]
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"description": "UUID for the filesystem",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ptuuid": {
|
"ptuuid": {
|
||||||
"description": "UUID for the disk image's partition table",
|
"description": "UUID for the disk image's partition table",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"pttype": {
|
||||||
|
"description": "The type of the partition table",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["mbr", "gpt"]
|
||||||
|
},
|
||||||
"root_fs_uuid": {
|
"root_fs_uuid": {
|
||||||
"description": "UUID for the root filesystem",
|
"description": "UUID for the root filesystem",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|
@ -86,8 +144,7 @@ def mkfs_for_type(device, uuid, fs_type):
|
||||||
maker(device, uuid)
|
maker(device, uuid)
|
||||||
|
|
||||||
|
|
||||||
def create_partition_table(image, options):
|
def create_partition_table_legacy(image, options):
|
||||||
"""Set up the partition table of the image"""
|
|
||||||
ptuuid = options["ptuuid"]
|
ptuuid = options["ptuuid"]
|
||||||
root_fs_uuid = options["root_fs_uuid"]
|
root_fs_uuid = options["root_fs_uuid"]
|
||||||
root_fs_type = options.get("root_fs_type", "ext4")
|
root_fs_type = options.get("root_fs_type", "ext4")
|
||||||
|
|
@ -112,6 +169,44 @@ def create_partition_table(image, options):
|
||||||
return partitions
|
return partitions
|
||||||
|
|
||||||
|
|
||||||
|
def create_partition_table(image, options):
|
||||||
|
"""Set up the partition table of the image"""
|
||||||
|
ptuuid = options["ptuuid"]
|
||||||
|
pttype = options.get("pttype")
|
||||||
|
|
||||||
|
# if 'pttype' is missing, we are in legacy mode
|
||||||
|
if pttype is None:
|
||||||
|
return create_partition_table_legacy(image, options)
|
||||||
|
|
||||||
|
# new mode
|
||||||
|
partitions = options["partitions"]
|
||||||
|
|
||||||
|
# generate the command for sfdisk to create the table
|
||||||
|
command = f"label: {pttype}\nlabel-id: {ptuuid}"
|
||||||
|
for partition in partitions:
|
||||||
|
fields = []
|
||||||
|
for field in ["start", "size", "type"]:
|
||||||
|
if field in partition:
|
||||||
|
fields += [f'{field}="{partition[field]}"']
|
||||||
|
if "bootable" in partition and partition["bootable"]:
|
||||||
|
fields += ["bootable"]
|
||||||
|
command += "\n" + ", ".join(fields)
|
||||||
|
|
||||||
|
subprocess.run(["sfdisk", "-q", image], input=command, encoding='utf-8', check=True)
|
||||||
|
|
||||||
|
# read the actual dimensions back
|
||||||
|
r = subprocess.run(["sfdisk", "--json", image], stdout=subprocess.PIPE, encoding='utf-8', check=True)
|
||||||
|
disk_table = json.loads(r.stdout)["partitiontable"]
|
||||||
|
disk_partitions = disk_table["partitions"]
|
||||||
|
|
||||||
|
assert len(disk_partitions) == len(partitions)
|
||||||
|
for i, partition in enumerate(partitions):
|
||||||
|
partition["start"] = disk_partitions[i]["start"] * 512
|
||||||
|
partition["size"] = disk_partitions[i]["size"] * 512
|
||||||
|
|
||||||
|
return partitions
|
||||||
|
|
||||||
|
|
||||||
def install_grub2(image, partitions):
|
def install_grub2(image, partitions):
|
||||||
"""Install grub2 to image"""
|
"""Install grub2 to image"""
|
||||||
grub2_core = "/var/tmp/grub2-core.img"
|
grub2_core = "/var/tmp/grub2-core.img"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue