App-FuguVM

 view release on metacpan or  search on metacpan

lib/App/FuguVM/Guest.pod  view on Meta::CPAN

=head1 NAME

App::FuguVM::Guest - the lifecycle of one OpenBSD virtual machine

=head1 SYNOPSIS

    use App::FuguVM::Guest;

    my $vm = App::FuguVM::Guest->new(
        config => $config->load_vm('default'),
        state  => $state,
        log    => $log,
    );

    exit $vm->up;

=head1 DESCRIPTION

The module runs QEMU for an OpenBSD guest, installs the system on the
first run, and stops it again without corrupting its disk.

Everything that is not QEMU comes from Fugu. Processes and liveness
come from L<Fugu::Process>, which reaps: a QEMU that became a
zombie reads as stopped, and a caller that is about to start a second
one needs that answer. Bounded waits come from L<Fugu::Timeout>. The
random root password comes from L<Fugu::Random>. The guest
connection comes from L<Fugu::SSH>.

=head1 METHODS

=head2 new

    App::FuguVM::Guest->new(config => \%vm, state => $state, log => $log, ...)

Build a controller. C<emulate> forces TCG instead of hardware
acceleration; C<no_cache> ignores the installed-image cache.

=head2 up

Make sure the VM runs, and do whatever that needs: verify the backing
chain, check the disk after an unclean shutdown, restore from the
installed-image cache, start the proxy, download the miniroot, create
the disk, start QEMU, install OpenBSD, and install the SSH key.

The method is idempotent. A VM that already runs, and whose SSH key is
current, returns success and changes nothing.

=head1 INSTALL MODES

C<install_mode> of the configuration selects the origin of the disk.
L<App::FuguVM::Config> derives the value from the directives.

In the C<expect> mode, C<up> installs with F<install.exp> over the
serial console. A failed install script gives exit code 9.

In the C<autoinstall> mode, C<up> starts the mirror proxy, starts the
L<App::FuguVM::Autoinstall> responder, and starts QEMU with the
miniroot attached and with C<-no-reboot>. F<autoinstall.exp> then
answers the install prompt with C<a> and types the response-file URL.
The guest fetches the file through the QEMU gateway, applies every
answer, and reboots itself; C<-no-reboot> makes QEMU exit instead, so
the guest cannot install a second time, and the exit leaves a
consistent disk. C<up> publishes the disk as a cache entry, exactly
as after an expect install, and it stops the responder on every path
out of the install. C<down> and C<destroy> also stop it. A failed
autoinstall script gives exit code 9.

In the C<import> mode, C<up> installs nothing. On the first run it
publishes the C<base_disk> file as the cache entry of the derived
key, with C<qemu-img convert>, and then overlays the working disk on
the published entry. A later C<up> reads the file no more. The tool
never writes to the source file. Every guest of the project derives
the same key, and the population lock of the cache serializes the
publication, so a parallel fleet publishes one time. Every cache verb
and snapshot verb works on the imported entry. A cache miss with an
absent source gives exit code 1, and the message names the path and
the key.

=head1 THE VERIFICATION OF AN INSTALL

Before the first boot, C<up> builds an L<App::FuguVM::Mirror> over
the version, the architecture, the C<verify> switch and the
C<signify_dir> directive of the configuration, and
L<App::FuguVM::Miniroot> downloads the install media through it. The
mirror proves the C<SHA256> manifest of the release under the release

lib/App/FuguVM/Guest.pod  view on Meta::CPAN

tool logs a warning and probes without the lock. The stop verbs
clear the record, so a later C<start> resolves again. C<stop> on a
guest that does not run also clears the record that a crash left
behind.

=head1 VERSION GATE

The optional C<qemu_version> directive pins the version of the QEMU
system binary. C<up> and C<start> enforce it one time for each
invocation, before the first spawn. The tool runs the binary that
the architecture selects with C<--version>, under a deadline of 10
seconds, and parses the first dotted-decimal token of the first
output line.

The match runs component by component, over the components that the
directive names: C<9.0> accepts C<9.0.4> and refuses C<9.1.0>, and
C<9.0.4> accepts C<9.0.4> only. A mismatch gives exit code 3, with a
message that names both versions. A pinned version that the tool
cannot verify fails closed with the same code: an absent binary, and
output with no version in it, both refuse the start. With no
directive the tool checks nothing, and it does not run the binary.

=head1 ARCHITECTURE

The C<arch> value of the configuration selects the QEMU binary, the
machine type, the firmware, and the TCG CPU model, through
L<App::FuguVM::Arch>. C<up> and C<start> exit with code 3 when the
QEMU binary of the architecture is not on C<PATH>.

An arm64 guest boots its firmware code file with C<-bios>. An amd64
guest boots through two pflash devices: the code file read-only, and
a fresh copy of its variable-store template in the state directory.
The copy is throwaway state, and every start makes it again. A guest
does not start without a firmware file, and the message names the
architecture.

The accelerator follows the host. KVM and HVF need a host machine
that runs the instruction set of the guest. KVM serves Linux with a
writable F</dev/kvm>, and HVF serves Darwin. Every other case, an
OpenBSD host included, uses TCG software emulation. C<host> CPU
passthrough pairs with hardware acceleration, and the TCG model of
the architecture pairs with TCG.

A disk belongs to one architecture for its whole life. C<up> and
C<start> stop with an error when the state records an other
architecture for the existing disk, and the message names
C<fuguvm destroy>.

=head1 SHUTDOWN

A shutdown syncs the guest filesystems first, then asks the guest to
power off through ACPI, and only then forces the process to stop.
Every step is bounded: a guest that stops answering must not hold the
caller.

The order matters. A force stop of a guest with unwritten buffers
leaves a filesystem that the next boot has to repair.

=head1 SEE ALSO

L<Fugu::Process>, L<Fugu::SSH>, L<Fugu::Timeout>,
L<App::FuguVM::Arch>, L<App::FuguVM::Disk>, L<App::FuguVM::DiskCache>,
L<App::FuguVM::QMP>, L<App::FuguVM::State>, L<fuguvm(1)>

=head1 AUTHOR

Dick Olsson <hi@senzilla.io>



( run in 1.803 second using v1.01-cache-2.11-cpan-85d3896f969 )