For all currently supported modules, i.e. stages and assemblers,
convert the STAGE_DESC and STAGE_INFO into a proper doc-string.
Rename the STAGE_OPTS into SCHEMA.
Refactor meta.ModuleInfo loading accordingly.
The script to be used for the conversion is:
--- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< --- 8< ---
import os
import sys
import osbuild
import osbuild.meta
from osbuild.meta import ModuleInfo
def find_line(lines, start):
for i, l in enumerate(lines):
if l.startswith(start):
return i
return None
def del_block(lines, prefix):
start = find_line(lines, prefix)
end = find_line(lines[start:], '"""')
print(start, end)
del lines[start:start+end+1]
def main():
index = osbuild.meta.Index(os.curdir)
modules = []
for klass in ("Stage", "Assembler"):
mods = index.list_modules_for_class(klass)
modules += [(klass, module) for module in mods]
for m in modules:
print(m)
klass, name = m
info = ModuleInfo.load(os.curdir, klass, name)
module_path = ModuleInfo.module_class_to_directory(klass)
path = os.path.join(os.curdir, module_path, name)
with open(path, "r") as f:
data = list(f.readlines())
i = find_line(data, "STAGE_DESC")
print(i)
del data[i]
del_block(data, "STAGE_INFO")
i = find_line(data, "STAGE_OPTS")
data[i] = 'SCHEMA = """\n'
docstr = '"""\n' + info.desc + "\n" + info.info + '"""\n'
doclst = docstr.split("\n")
doclst = [l + "\n" for l in doclst]
data = [data[0]] + doclst + data[1:]
with open(path, "w") as f:
f.writelines(data)
if __name__ == "__main__":
main()
103 lines
3.1 KiB
Python
Executable file
103 lines
3.1 KiB
Python
Executable file
#!/usr/bin/python3
|
|
"""
|
|
Assemble tree into a raw filesystem image
|
|
|
|
Assemble the tree into a raw filesystem image named `filename`, with the UUID
|
|
`root_fs_uuid`.
|
|
|
|
The image is a sparse file of the given `size`, which is created using the
|
|
`truncate(1)` command. The `size` is an integer with an optional suffix:
|
|
K,M,G,T,... (for powers of 1024) or KB,MB,GB,TB,... (powers of 1000).
|
|
|
|
NOTE: If the tree contents are larger than `size`, this assembler will fail.
|
|
On the other hand, since the image is a sparse file, the unused parts of the
|
|
image take up almost no disk space - so a 1GB tree in a 20GB image should not
|
|
use much more than 1GB disk space.
|
|
|
|
The filesystem UUID should be a standard (RFC4122) UUID, which you can
|
|
generate with uuid.uuid4() in Python, `uuidgen(1)` in a shell script, or
|
|
read from `/proc/sys/kernel/random/uuid` if your kernel provides it.
|
|
"""
|
|
|
|
|
|
import contextlib
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import osbuild.remoteloop as remoteloop
|
|
|
|
SCHEMA = """
|
|
"additionalProperties": false,
|
|
"required": ["filename", "root_fs_uuid", "size"],
|
|
"properties": {
|
|
"filename": {
|
|
"description": "Raw filesystem image filename",
|
|
"type": "string"
|
|
},
|
|
"root_fs_uuid": {
|
|
"description": "UUID for the filesystem",
|
|
"type": "string",
|
|
"pattern": "^[0-9A-Za-z]{8}(-[0-9A-Za-z]{4}){3}-[0-9A-Za-z]{12}$",
|
|
"examples": ["9c6ae55b-cf88-45b8-84e8-64990759f39d"]
|
|
},
|
|
"size": {
|
|
"description": "Maximum size of the filesystem",
|
|
"type": "integer"
|
|
},
|
|
"fs_type": {
|
|
"description": "Filesystem type",
|
|
"type": "string",
|
|
"enum": ["ext4", "xfs"],
|
|
"default": "ext4"
|
|
}
|
|
}
|
|
"""
|
|
|
|
@contextlib.contextmanager
|
|
def mount(source, dest, *options):
|
|
os.makedirs(dest, 0o755, True)
|
|
subprocess.run(["mount", *options, source, dest], check=True)
|
|
try:
|
|
yield
|
|
finally:
|
|
subprocess.run(["umount", "-R", dest], check=True)
|
|
|
|
|
|
def mkfs_ext4(device, uuid):
|
|
subprocess.run(["mkfs.ext4", "-U", uuid, device], input="y", encoding='utf-8', check=True)
|
|
|
|
|
|
def mkfs_xfs(device, uuid):
|
|
subprocess.run(["mkfs.xfs", "-m", f"uuid={uuid}", device], encoding='utf-8', check=True)
|
|
|
|
|
|
def main(tree, output_dir, options, loop_client):
|
|
filename = options["filename"]
|
|
root_fs_uuid = options["root_fs_uuid"]
|
|
size = options["size"]
|
|
fs_type = options.get("fs_type", "ext4")
|
|
|
|
image = f"/var/tmp/osbuild-image.raw"
|
|
mountpoint = f"/tmp/osbuild-mnt"
|
|
|
|
subprocess.run(["truncate", "--size", str(size), image], check=True)
|
|
|
|
if fs_type == "ext4":
|
|
mkfs_ext4(image, root_fs_uuid)
|
|
elif fs_type == "xfs":
|
|
mkfs_xfs(image, root_fs_uuid)
|
|
else:
|
|
raise ValueError("`fs_type` must be either ext4 or xfs")
|
|
|
|
# Copy the tree into the target image
|
|
with loop_client.device(image) as loop, mount(loop, mountpoint):
|
|
subprocess.run(["cp", "-a", f"{tree}/.", mountpoint], check=True)
|
|
|
|
subprocess.run(["mv", image, f"{output_dir}/{filename}"], check=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
args = json.load(sys.stdin)
|
|
r = main(args["tree"], args["output_dir"], args["options"], remoteloop.LoopClient("/run/osbuild/api/remoteloop"))
|
|
sys.exit(r)
|