| OpenHVF::ImageCache(3p) | Perl Library Manual | OpenHVF::ImageCache(3p) |
NAME
OpenHVF::ImageCache - cache of installed OpenBSD disk images
SYNOPSIS
use OpenHVF::ImageCache;
my $cache = OpenHVF::ImageCache->new('~/.cache/openhvf');
my $key = $cache->key($vm_config);
# Boot from a previous installation
if (my $hit = $cache->lookup($key)) {
$disk->create($name, undef, $hit->{base}, 'qcow2');
$state->set_root_password($hit->{meta}{root_password});
}
# Publish a freshly installed disk
$cache->store($key, $disk_path, { root_password => $password });
for my $entry (@{ $cache->list }) {
printf "%s %d bytes\n", $entry->{key}, $entry->{size};
}
DESCRIPTION
Installing OpenBSD under TCG emulation costs tens of minutes. This module keeps the result so later runs skip it: a pristine, compacted copy of the disk taken the moment the installer finished, which subsequent VMs use as the backing image of a throwaway qcow2 overlay.
Entries live under the configured "cache_dir", beside the proxy cache that holds the miniroot and install sets:
<cache_dir>/installed/<key>/base.qcow2 mode 0400
<cache_dir>/installed/<key>/meta.json mode 0600
<cache_dir>/installed/<key>/snapshots/<name>.qcow2 mode 0400
<cache_dir>/installed/<key>/snapshots/<name>.json mode 0600
Entries are immutable and write-once. "store" builds one whole in a sibling ".tmp.*" directory and publishes it by renaming that directory, so a reader never sees one installation's base image beside another installation's metadata - a mismatch that looks live and then wedges every later boot with a root password that does not open the image.
Cache keys
"key" returns "<version>-<arch>-<hash8>", where "hash8" is a truncated SHA-256 over everything that shapes an installed disk: the OpenBSD version, the architecture, the disk size, the contents of the install.exp script that OpenHVF::Expect resolves, and the contents of share/openhvf/cache-generation.
That last file is a generation counter. Bump the number in it when the install driver changes in a way the install.exp hash cannot see - for instance a change to how OpenHVF::VM or OpenHVF::Expect drives the installer. It is a data file rather than a constant in this module so a continuous-integration cache key can hash it too; keep it to the bare number, since its whole content is hashed and a comment edit would rotate the key.
Memory and port settings do not shape the disk and are deliberately excluded, so changing them still hits the same entry.
Snapshots
A snapshot is an immutable named layer over an entry's base image, for caching states openhvf itself knows nothing about - the motivating case is a provisioning script caching its "guest packages installed" state. The mechanism lives here; the policy of what is worth caching stays in the callers.
Snapshots live inside "installed/<key>/", so invalidating a base - a new key, or "cache clear" - invalidates the snapshots that depend on that exact base file, for free.
"snapshot_store" flattens the working disk onto "base.qcow2" rather than copying it, so every snapshot is a direct child of the base:
base.qcow2 <- <name>.qcow2 <- disk.qcow2
A copy would carry the working disk's backing-file header verbatim, which is only correct while that disk hangs directly off the base. After a restore it hangs off a snapshot, so a copy would either stack chains without bound or - when the same name is re-saved, which a normal second run does - publish a qcow2 naming itself as its own backing file. Flattening also means no snapshot is ever another's parent, so "snapshot_remove" cannot orphan one.
The working disk must be from a stopped VM: a live overlay is not consistent.
METHODS
- new($cache_dir)
- Construct a cache over $cache_dir, expanding a leading "~".
- cache_dir
- installed_dir
- entry_dir($key)
- base_path($key)
- Locations, whether or not they exist.
- key($vm_config)
- Derive the cache key for a VM configuration hash, or "undef" when an input cannot be read - which leaves the caller with no key, and therefore no caching.
- lookup($key)
- Return "{ key, dir, base, meta }" for a complete entry, "undef" otherwise. A half-written entry is a miss, not an error.
- store($key, $disk_path, $meta)
- Compact $disk_path into the cache as the base image for $key and write $meta - which should carry the guest "root_password" - alongside it. Returns the base image path, or "undef" on any failure, including an attempt to overwrite a populated key. The caller degrades to a standalone disk.
- list
- Every complete entry, newest first, as "{ key, dir, base, meta, size, created_at, snapshots }".
- remove($key)
- Delete an entry and everything under it. True when the entry is gone.
- sweep_temp
- Remove ".tmp.*" trees left behind by an interrupted "store", including those of earlier processes. Returns the count removed.
- key_for_path($path)
- The key of the entry containing $path - a base image or a snapshot - or "undef" when $path lies outside the cache. Answers "which cached image is this working disk built on?".
- snapshot_dir($key)
- snapshot_path($key, $name)
- Locations, whether or not they exist.
- valid_snapshot_name($name)
- Whether $name is usable. Names become file names inside the cache, so they must start with an alphanumeric and hold to word characters, dots and dashes, within a bounded length.
- snapshot_store($key, $name, $disk_path, $meta)
- Flatten the stopped working disk at $disk_path
onto $key's base image and publish it as the named
layer. $meta should carry the state fields the
disk embodies - "installed" and the
installed SSH public key - so a restore can reseed OpenHVF::State; the
root password is copied from the base's own metadata rather than trusted
from the caller. Returns the snapshot path, or
"undef" on failure.
Re-saving an existing name replaces it, which is the normal second run of a provisioning script.
- snapshot_lookup($key, $name)
- Return "{ key, name, path, base, meta }" for a snapshot whose image, its metadata, and its backing chain all resolve; "undef" otherwise. A snapshot whose base has been removed is a miss, not an error, so a caller can fall back to provisioning from scratch instead of failing hard.
- snapshot_list($key)
- Sorted snapshots of an entry, as "{ name, path, size, created_at, meta }".
- snapshot_remove($key, $name)
- Delete a snapshot and its metadata. Safe in any order: snapshots are always direct children of the base, never of each other.
SECURITY
meta.json is mode 0600 and holds the generated guest root password, the same secret the VM state directory already keeps. Caching lengthens its life: it survives "openhvf destroy" and rotates only when the base key does. The guest permits password root login and QEMU forwards its SSH and serial console ports on every host interface, so the password is not a localhost-only secret. See openhvf(1).
SEE ALSO
OpenHVF::Disk, OpenHVF::Expect, OpenHVF::Image, OpenHVF::VM, openhvf(1)
| 2026-07-27 | OpenBSD |