directory: Optional[Path]
options: Optional[str]
file_id: str
+ persist: bool
# We use negative numbers for specifying special constants
def parse_drive(value: str) -> Drive:
- parts = value.split(":", maxsplit=4)
- if not parts or not parts[0]:
+ parts = value.split(":")
+
+ if len(parts) > 6:
+ die(f"Too many components in drive '{value}")
+
+ if len(parts) < 1:
die(f"No ID specified for drive '{value}'")
if len(parts) < 2:
die(f"Missing size in drive '{value}")
- if len(parts) > 5:
- die(f"Too many components in drive '{value}")
-
id = parts[0]
if not is_valid_filename(id):
die(f"Unsupported path character in drive id '{id}'")
- size = parse_bytes(parts[1])
-
- directory = parse_path(parts[2]) if len(parts) > 2 and parts[2] else None
- options = parts[3] if len(parts) > 3 and parts[3] else None
- file_id = parts[4] if len(parts) > 4 and parts[4] else id
-
- return Drive(id=id, size=size, directory=directory, options=options, file_id=file_id)
+ return Drive(
+ id=id,
+ size=parse_bytes(parts[1]),
+ directory=parse_path(p) if len(parts) > 2 and (p := parts[2]) else None,
+ options=p if len(parts) > 3 and (p := parts[3]) else None,
+ file_id=p if len(parts) > 4 and (p := parts[4]) else id,
+ persist=parse_boolean(p) if len(parts) > 5 and (p := parts[5]) else False,
+ )
def config_parse_sector_size(value: Optional[str], old: Optional[int]) -> Optional[int]:
directory=Path(d["Directory"]) if d.get("Directory") else None,
options=d.get("Options"),
file_id=d.get("FileId", d["Id"]),
+ persist=d.get("Persist", False),
)
)
import uuid
from collections.abc import Iterator, Sequence
from pathlib import Path
-from typing import Optional
+from typing import IO, Optional
from mkosi.config import (
Args,
@contextlib.contextmanager
def finalize_drive(config: Config, drive: Drive) -> Iterator[Path]:
- with tempfile.NamedTemporaryFile(
- dir=drive.directory or "/var/tmp",
- prefix=f"mkosi-drive-{drive.id}",
- ) as file:
+ with contextlib.ExitStack() as stack:
+ file: IO[bytes]
+ if drive.persist:
+ path = Path(drive.directory or "/var/tmp") / f"mkosi-drive-{drive.id}"
+ file = path.open("a+b")
+ else:
+ file = stack.enter_context(
+ tempfile.NamedTemporaryFile(
+ dir=drive.directory or "/var/tmp",
+ prefix=f"mkosi-drive-{drive.id}",
+ )
+ )
maybe_make_nocow(Path(file.name))
file.truncate(round_up(drive.size, resource.getpagesize()))
yield Path(file.name)
`Drives=`, `--drive=`
: Add a drive. Takes a colon-delimited string of format
- `<id>:<size>[:<directory>[:<options>[:<file-id>]]]`. `id` specifies
+ `<id>:<size>[:<directory>[:<options>[:<file-id>[:<persist>]]]]`. `id` specifies
the ID assigned to the drive. This can be used as the `drive=`
property in various **qemu** devices. `size` specifies the size of the
drive. This takes a size in bytes. Additionally, the suffixes `K`, `M`
and `G` can be used to specify a size in kilobytes, megabytes and
gigabytes respectively. `directory` optionally specifies the directory
- in which to create the file backing the drive. `options` optionally
- specifies extra comma-delimited properties which are passed verbatim
+ in which to create the file backing the drive. If unset, the file will be created under `/var/tmp`.
+ `options` optionally specifies extra comma-delimited properties which are passed verbatim
to **qemu**'s `-blockdev` option. `file-id` specifies the ID of the file
- backing the drive. Drives with the same file ID will share the
- backing file. The directory and size of the file will be determined
- from the first drive with a given file ID.
+ backing the drive. If unset, this defaults to the drive ID.
+ Drives with the same file ID will share the backing file.
+ The directory and size of the file will be determined from the first drive with a given file ID.
+ `persist` takes a boolean value and determines whether the drive will be persisted across **qemu** invocations.
+ Enabling persistence also prevents suffixing the filename with a random string.
+ The file backing the drive will always be available under `/<directory>/mkosi-drive-<file-id>`
+ You can skip values by setting them to the empty string, specifying e.g. `myfs:1G::::yes`
+ will create a persistent drive under `/var/tmp/mkosi-drive-myfs`.
**Example usage:**
"FileId": "red",
"Id": "abc",
"Options": "abc,qed",
+ "Persist": false,
"Size": 200
},
{
"FileId": "wcd",
"Id": "abc",
"Options": "",
+ "Persist": false,
+ "Size": 200
+ },
+ {
+ "Directory": null,
+ "FileId": "bla",
+ "Id": "abc",
+ "Options": "",
+ "Persist": true,
"Size": 200
}
],
credentials={"credkey": "credval"},
dependencies=["dep1"],
distribution=Distribution.fedora,
- drives=[Drive("abc", 200, Path("/foo/bar"), "abc,qed", "red"), Drive("abc", 200, None, "", "wcd")],
+ drives=[
+ Drive("abc", 200, Path("/foo/bar"), "abc,qed", "red", False),
+ Drive("abc", 200, None, "", "wcd", False),
+ Drive("abc", 200, None, "", "bla", True),
+ ],
environment_files=[],
environment={"foo": "foo", "BAR": "BAR", "Qux": "Qux"},
ephemeral=True,