vagrant-qemu 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +69 -0
- data/README.md +202 -17
- data/lib/vagrant-qemu/action/cleanup_virtiofs.rb +18 -0
- data/lib/vagrant-qemu/action/mount_virtiofs.rb +35 -0
- data/lib/vagrant-qemu/action/prepare_forwarded_port_collision_params.rb +17 -11
- data/lib/vagrant-qemu/action/reset_virtiofs_args.rb +40 -0
- data/lib/vagrant-qemu/action/start_instance.rb +69 -5
- data/lib/vagrant-qemu/action.rb +8 -0
- data/lib/vagrant-qemu/cap/disk.rb +46 -1
- data/lib/vagrant-qemu/command/serial_config.rb +52 -0
- data/lib/vagrant-qemu/config.rb +22 -3
- data/lib/vagrant-qemu/driver.rb +61 -12
- data/lib/vagrant-qemu/errors.rb +6 -0
- data/lib/vagrant-qemu/plugin.rb +10 -0
- data/lib/vagrant-qemu/synced_folder_virtiofs.rb +259 -0
- data/lib/vagrant-qemu/version.rb +1 -1
- data/locales/en.yml +21 -0
- metadata +7 -56
- data/.gitignore +0 -24
- data/CLAUDE.md +0 -81
- data/Gemfile +0 -16
- data/Rakefile +0 -42
- data/spec/acceptance/basic_up_spec.rb +0 -83
- data/spec/acceptance/halt_destroy_spec.rb +0 -69
- data/spec/acceptance/helper.rb +0 -69
- data/spec/acceptance/network_spec.rb +0 -134
- data/spec/acceptance/port_collision_spec.rb +0 -53
- data/spec/e2e/advanced_network_spec.rb +0 -123
- data/spec/e2e/disk_spec.rb +0 -107
- data/spec/e2e/forwarded_port_spec.rb +0 -107
- data/spec/e2e/halt_spec.rb +0 -48
- data/spec/e2e/helper.rb +0 -161
- data/spec/e2e/package_spec.rb +0 -95
- data/spec/e2e/provision_spec.rb +0 -35
- data/spec/e2e/reload_spec.rb +0 -58
- data/spec/e2e/smoke_spec.rb +0 -60
- data/spec/e2e/socket_network_spec.rb +0 -214
- data/spec/e2e/socket_vmnet_spec.rb +0 -131
- data/spec/e2e/support/qemu_no_stream_shim.sh +0 -14
- data/spec/spec_helper.rb +0 -77
- data/spec/unit/action/cloud_init_network_spec.rb +0 -177
- data/spec/unit/action/destroy_spec.rb +0 -46
- data/spec/unit/action/export_spec.rb +0 -103
- data/spec/unit/action/import_spec.rb +0 -52
- data/spec/unit/action/package_spec.rb +0 -22
- data/spec/unit/action/package_vagrantfile_spec.rb +0 -41
- data/spec/unit/action/prepare_forwarded_port_collision_params_spec.rb +0 -104
- data/spec/unit/action/read_state_spec.rb +0 -50
- data/spec/unit/action/start_instance_port_spec.rb +0 -77
- data/spec/unit/action/start_instance_spec.rb +0 -64
- data/spec/unit/action/warn_networks_spec.rb +0 -38
- data/spec/unit/cap/disk_spec.rb +0 -104
- data/spec/unit/config_spec.rb +0 -186
- data/spec/unit/driver/delete_spec.rb +0 -39
- data/spec/unit/driver/options_yaml_spec.rb +0 -49
- data/spec/unit/driver/package_spec.rb +0 -51
- data/spec/unit/driver/ssh_port_spec.rb +0 -46
- data/spec/unit/driver/start_cmd_advanced_spec.rb +0 -120
- data/spec/unit/driver/start_cmd_order_spec.rb +0 -67
- data/spec/unit/driver/start_cmd_socket_vmnet_spec.rb +0 -144
- data/spec/unit/driver/start_cmd_spec.rb +0 -161
- data/spec/unit/driver/start_edge_cases_spec.rb +0 -72
- data/spec/unit/driver/state_spec.rb +0 -60
- data/spec/unit/driver/stop_spec.rb +0 -101
- data/spec/unit/network/backend_for_spec.rb +0 -31
- data/spec/unit/network/build_network_config_spec.rb +0 -49
- data/spec/unit/network/generate_mac_spec.rb +0 -26
- data/spec/unit/network/socket_spec.rb +0 -44
- data/spec/unit/network/socket_vmnet_spec.rb +0 -146
- data/spec/unit/network/tap_spec.rb +0 -15
- data/spec/unit/network/vmnet_spec.rb +0 -64
- data/vagrant-qemu.gemspec +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 462be36106f57c7732a3376ec6984c823196782401f5bd0d5efd6ef14869b525
|
|
4
|
+
data.tar.gz: d4e3ef79a87a6349fb48a195c02252de0911ab0c3682dfba6552db011d248d39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ded7ec9fcddf013a9acb8caa8962bb4d84cd6f6126b306cdba508fca1ffe6457e9aa0e58c4594c9de5dccb74d6615da78d4733d2cdfb95d7aa21553cbf03fc00
|
|
7
|
+
data.tar.gz: 3fa5f0ddddaeec9f9c26199ef80695760dffd65294b8ff2218257f3376ebe831ac62665ca26a6810116017bb612fe490d7521783d76438f63435532ac3e5e361
|
data/CHANGELOG.md
CHANGED
|
@@ -223,3 +223,72 @@
|
|
|
223
223
|
* README: bring a `socket_opts` `listen=`/`connect=` pair up with
|
|
224
224
|
`vagrant up --no-parallel`. A plain `vagrant up` boots both machines in
|
|
225
225
|
parallel, and if the connector starts first the link never comes up.
|
|
226
|
+
|
|
227
|
+
# 0.7.0 (2026-10-15)
|
|
228
|
+
|
|
229
|
+
Thanks to @christhomas, who contributed most of this release
|
|
230
|
+
(#81, #85, #87–#95).
|
|
231
|
+
|
|
232
|
+
New:
|
|
233
|
+
|
|
234
|
+
* VirtioFS synced folders: `config.vm.synced_folder ..., type: "qemu_virtiofs"`.
|
|
235
|
+
The provider starts one `virtiofsd` per share, backs guest RAM with a shared
|
|
236
|
+
memory file and mounts the shares after boot; the daemons are stopped on
|
|
237
|
+
halt, destroy, package and failed boots. New config: `virtiofsd_bin`,
|
|
238
|
+
`virtiofs_guest_uid` / `virtiofs_guest_gid` (default 1000) and
|
|
239
|
+
`extra_virtiofsd_args`; `qe.memory` must be a positive size. Linux hosts and
|
|
240
|
+
WSL2 use the distribution's `virtiofsd`. macOS support is experimental and
|
|
241
|
+
needs a QEMU built with vhost-user and a macOS `virtiofsd` port, available
|
|
242
|
+
from a community Homebrew tap (see the README). (#81)
|
|
243
|
+
* `vagrant serial-config [name|id] [--json]` prints the Unix socket path or TCP
|
|
244
|
+
host and port of each QEMU machine's serial console. (#37, #92)
|
|
245
|
+
* README: how to package a QEMU machine into a reusable box, plus a tested
|
|
246
|
+
round-trip recipe in `examples/package-roundtrip`. (#94)
|
|
247
|
+
|
|
248
|
+
Behavior changes:
|
|
249
|
+
|
|
250
|
+
* If the guest does not become reachable within `config.vm.boot_timeout`
|
|
251
|
+
(default 300 s), or Vagrant reports an invalid boot state, the provider now
|
|
252
|
+
stops the QEMU instance it just started and keeps its disks. Previously QEMU
|
|
253
|
+
was left running. Emulated guests (a guest architecture different from the
|
|
254
|
+
host, or no HVF/KVM) and several VMs booting in parallel can need more than
|
|
255
|
+
300 s; set `config.vm.boot_timeout = 600` or more. A guest that used to
|
|
256
|
+
finish booting in the background after the timeout now has to be brought up
|
|
257
|
+
again with `vagrant up`, from the start. Interrupting the boot wait with
|
|
258
|
+
Ctrl-C still leaves QEMU running. (#89, refs #21)
|
|
259
|
+
* `config.vm.disk :disk, primary: true, size: ...` now grows the existing boot
|
|
260
|
+
disk instead of attaching a new blank disk; it never shrinks it, and reports
|
|
261
|
+
sizes such as `100 GiB`. The blank disk an older version created for the
|
|
262
|
+
declaration (`vagrant_primary.qcow2`, or `<name>.qcow2` for a custom name)
|
|
263
|
+
is no longer attached; the provider warns while that file exists, and
|
|
264
|
+
the README's "Upgrading older primary disk declarations" section shows how to
|
|
265
|
+
attach it again. (#66, #88, #93, #95)
|
|
266
|
+
* With `net_device = nil`, an inherited SSH forward is disabled and the
|
|
267
|
+
configured `ssh_port` is kept instead of a collision-corrected one. (#71, #87)
|
|
268
|
+
|
|
269
|
+
Fixes:
|
|
270
|
+
|
|
271
|
+
* macOS: firmware for an aarch64 guest is now looked up in the host's Homebrew
|
|
272
|
+
prefix rather than one chosen by the guest architecture, fixing Intel Macs.
|
|
273
|
+
If Vagrant's Ruby runs under Rosetta on Apple Silicon, set `HOMEBREW_PREFIX`
|
|
274
|
+
or `qe.qemu_dir`. (#50, #59, #90)
|
|
275
|
+
* Waiting for a monitor reply during `vagrant halt` is now bounded, so halt can
|
|
276
|
+
always escalate to quit and kill. With `control_port` set, halt connects to
|
|
277
|
+
127.0.0.1: on macOS 27 connecting to localhost could pick ::1 and appear
|
|
278
|
+
connected, so powerdown and quit were never delivered and QEMU was killed
|
|
279
|
+
only after both timeouts. (#89)
|
|
280
|
+
* `vagrant halt` could fail with `Errno::ENOENT` when QEMU removed its pidfile
|
|
281
|
+
just as the provider read it. An empty pidfile no longer reads as a running
|
|
282
|
+
VM, and a force kill waits for QEMU to exit.
|
|
283
|
+
* Updating the SSH forward no longer leaks into other machines that share a
|
|
284
|
+
forwarded_port rule from the top-level or box Vagrantfile.
|
|
285
|
+
* README: AlmaLinux 9 x86_64 guests need `qe.cpu = "max"` under emulation
|
|
286
|
+
(#67, #91); the `other_default` default is `-parallel none`.
|
|
287
|
+
|
|
288
|
+
Project:
|
|
289
|
+
|
|
290
|
+
* Cross-platform CI on GitHub Actions: unit specs on Linux, macOS and Windows;
|
|
291
|
+
guest smoke tests on Linux, macOS and WSL2; e2e on Linux (KVM), macOS (TCG:
|
|
292
|
+
vmnet, socket_vmnet, VirtioFS) and VirtioFS in WSL2. (#85)
|
|
293
|
+
* The gem now ships only `lib/`, `locales/`, `LICENSE`, `README.md` and
|
|
294
|
+
`CHANGELOG.md`.
|
data/README.md
CHANGED
|
@@ -35,10 +35,12 @@ Others:
|
|
|
35
35
|
* SSH into VM
|
|
36
36
|
* Provision the instances with any built-in Vagrant provisioner
|
|
37
37
|
* Synced folder support via SMB
|
|
38
|
-
*
|
|
38
|
+
* Synced folder support via VirtioFS for Linux guests
|
|
39
|
+
* Basic operation: up, ssh, halt, destroy, package
|
|
39
40
|
* Basic suport to forwarded ports, see [vagrant doc](https://www.vagrantup.com/docs/networking/forwarded_ports) for details
|
|
40
41
|
* Support Cloud-init, see [vagrant doc](https://developer.hashicorp.com/vagrant/docs/cloud-init/usage) for details
|
|
41
42
|
* Support Disks, see [vagrant doc](https://developer.hashicorp.com/vagrant/docs/disks/usage) for details
|
|
43
|
+
* `config.vm.disk :disk, primary: true, size: "100GB"` grows the existing boot disk. The provider reports growth before resizing and warns when a smaller requested size is skipped; it never shrinks the disk. This resizes the virtual disk only: grow the guest's partition/filesystem separately, or use a box whose cloud-init handles that. The declaration is reapplied on start/reload, unlike `qe.disk_resize`, which applies during import. See the [upgrade note for older primary disk declarations](#upgrading-older-primary-disk-declarations).
|
|
42
44
|
* Advanced networking (opt-in): dual-NIC with `private_network` support via QEMU native vmnet (macOS), TAP (Linux), or a `socket` netdev — multicast (Linux/Windows) or point-to-point listen/connect (no root, works on macOS)
|
|
43
45
|
|
|
44
46
|
## Usage
|
|
@@ -66,6 +68,53 @@ Notes:
|
|
|
66
68
|
see [vagrant doc](https://www.vagrantup.com/docs/synced-folders/smb) for details
|
|
67
69
|
* need username/password to access shared folder
|
|
68
70
|
|
|
71
|
+
### VirtioFS synced folders
|
|
72
|
+
|
|
73
|
+
Install `virtiofsd` on the host and use a Linux guest with VirtioFS support.
|
|
74
|
+
The plugin searches `PATH`, `/usr/libexec`, and `/usr/lib/qemu` for the daemon;
|
|
75
|
+
set `qe.virtiofsd_bin` when it is installed elsewhere.
|
|
76
|
+
|
|
77
|
+
Apple Silicon macOS support is experimental. The community tap maintained by
|
|
78
|
+
christhomas supplies his macOS virtiofsd port and upstream QEMU built with
|
|
79
|
+
`--enable-vhost-user`. This QEMU formula conflicts with stock Homebrew `qemu`;
|
|
80
|
+
unlink or uninstall the stock formula before installing the tap build:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
brew unlink qemu # if the stock formula is installed
|
|
84
|
+
brew install antimatter-studios/tap/qemu antimatter-studios/tap/virtiofsd
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
CI also points the provider at the tap build's firmware directory. If
|
|
88
|
+
`vagrant up` cannot find the aarch64 firmware, do the same:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
export QEMU_DIR="$(brew --prefix antimatter-studios/tap/qemu)/share/qemu"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
CI pins the tap to `f51cb0fcd636247ea977898e7d01a893ee05cf86` and tests this
|
|
95
|
+
combination with TCG; it does not establish macOS HVF support. Linux and Windows WSL2 use the Ubuntu
|
|
96
|
+
`virtiofsd` package. In WSL2 both QEMU and the daemon run inside Linux, so
|
|
97
|
+
shares can live on the native Linux filesystem; Windows DrvFs paths are
|
|
98
|
+
not required. Use `type: "qemu_virtiofs"` explicitly, or restrict
|
|
99
|
+
`config.vm.allowed_synced_folder_types` to `[:qemu_virtiofs]`; its priority is
|
|
100
|
+
below rsync and NFS so installing the daemon does not override those types.
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
config.vm.synced_folder "./src", "/home/vagrant/src", type: "qemu_virtiofs"
|
|
104
|
+
config.vm.provider "qemu" do |qe|
|
|
105
|
+
qe.virtiofsd_bin = "/path/to/virtiofsd" # optional
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`virtiofs_guest_uid` and `virtiofs_guest_gid` default to `1000`. UID/GID
|
|
110
|
+
translation is used when the installed daemon supports it. Additional daemon
|
|
111
|
+
arguments can be passed through `extra_virtiofsd_args` as an array.
|
|
112
|
+
VirtioFS requires a positive `qe.memory`: bare numbers mean MiB, and
|
|
113
|
+
`K`, `M`, `G`, and `T` suffixes are accepted. The size is rounded up to a whole
|
|
114
|
+
MiB for both guest RAM and its shared backend (`"1.3G"` becomes `1332M`). `nil` is rejected for VirtioFS
|
|
115
|
+
because the shared memory backend needs an explicit size. QEMU creates an
|
|
116
|
+
unlinked RAM backing file in the host temporary directory.
|
|
117
|
+
|
|
69
118
|
## Box format
|
|
70
119
|
|
|
71
120
|
Same as [vagrant-libvirt version-1](https://github.com/vagrant-libvirt/vagrant-libvirt#version-1):
|
|
@@ -74,6 +123,40 @@ Same as [vagrant-libvirt version-1](https://github.com/vagrant-libvirt/vagrant-l
|
|
|
74
123
|
* `metadata.json` file describing box image (provider, virtual_size, format)
|
|
75
124
|
* `Vagrantfile` that does default settings
|
|
76
125
|
|
|
126
|
+
### Package a QEMU machine into a reusable box
|
|
127
|
+
|
|
128
|
+
From a source project's directory, start the QEMU VM and package it:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
vagrant up --provider=qemu
|
|
132
|
+
vagrant package --output ../portable.box
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
To package a named machine, pass its name before `--output`, for example
|
|
136
|
+
`vagrant package web --output ../portable.box`.
|
|
137
|
+
|
|
138
|
+
In a fresh consumer project, add the archive and select that box in its
|
|
139
|
+
`Vagrantfile`:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
vagrant box add portable-qemu ../portable.box
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
Vagrant.configure("2") do |config|
|
|
147
|
+
config.vm.box = "portable-qemu"
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Then boot it with `vagrant up --provider=qemu`. The archive uses the `libvirt`
|
|
152
|
+
box format, which this plugin can import; let `vagrant box add` read its provider
|
|
153
|
+
metadata. Packaging stops the source VM and includes its box disks and
|
|
154
|
+
architecture. Extra disks, DVDs and cloud-init seed ISOs are excluded. Set up
|
|
155
|
+
networking and synced folders in the consumer project.
|
|
156
|
+
|
|
157
|
+
For a tested source-to-consumer round trip, see the
|
|
158
|
+
[packaging recipe](examples/package-roundtrip/README.md).
|
|
159
|
+
|
|
77
160
|
## Configuration
|
|
78
161
|
|
|
79
162
|
### Options
|
|
@@ -87,10 +170,10 @@ This provider exposes a few provider-specific configuration options:
|
|
|
87
170
|
* `cpu` - The cpu model of VM, default: `host` for native virtualization, `max` when emulating a non-host arch
|
|
88
171
|
* `smp` - The smp setting (Simulate an SMP system with n CPUs) of VM, default: `2`
|
|
89
172
|
* `memory` - The memory setting of VM, default: `4G`
|
|
90
|
-
* `disk_resize` - The target disk size of the primary disk, requires resizing of filesystem inside of VM, default: `nil`.
|
|
173
|
+
* `disk_resize` - The target disk size of the primary disk, requires resizing of filesystem inside of VM, default: `nil`. Applied only when the VM is imported. With a `primary: true` disk declaration as well, the disk ends up at the larger of the two sizes, and if `disk_resize` is the larger one every start warns that the smaller declaration cannot shrink the disk; set only one of them.
|
|
91
174
|
* debug/expert
|
|
92
175
|
* `ssh_host` - The SSH IP used to access VM, default: `127.0.0.1`
|
|
93
|
-
* `ssh_auto_correct` - Auto correct port collisions for
|
|
176
|
+
* `ssh_auto_correct` - Auto correct port collisions for the provider's SSH forward, default: `false`. When `net_device = nil`, no synthetic SSH forward is added and the direct SSH port is preserved; configure `ssh_host` and `ssh_port` for your custom network.
|
|
94
177
|
* `net_device` - The network device, default: auto-detected — `virtio-net-device` (arm64 `virt`) or `virtio-net-pci` (x86_64 `q35`)
|
|
95
178
|
* `drive_interface` - The interface type for the main drive, default `virtio`
|
|
96
179
|
* `image_path` - The path (or array of paths) to qcow2 image for box-less VM, default is nil value
|
|
@@ -103,7 +186,7 @@ This provider exposes a few provider-specific configuration options:
|
|
|
103
186
|
* `debug_port` - The port number used to export serial port of the vm for debug, default is nil value. (nil means use unix socket, see "Debug" below for details)
|
|
104
187
|
* `no_daemonize` - Disable the "daemonize" mode of QEMU, default is false. (see "Windows host" below as example)
|
|
105
188
|
* `firmware_format` - The format of aarch64 firmware images (`edk2-aarch64-code.fd` and `edk2-arm-vars.fd`) loaded from `qemu_dir`, default: `raw`
|
|
106
|
-
* `other_default` - The other default arguments used by this plugin, default: `%W(-parallel
|
|
189
|
+
* `other_default` - The other default arguments used by this plugin, default: `%W(-parallel none -monitor none -display none -vga none)`
|
|
107
190
|
* `extra_image_opts` - Options passed via `-o` to `qemu-img` when the base qcow2 images are created, default: `[]`
|
|
108
191
|
* `graceful_timeout` - Seconds to wait at each `vagrant halt` stage before escalating, default: `60`. Halt sends ACPI `system_powerdown`, waits up to this long, then sends QEMU's `quit` monitor command (a clean shutdown that flushes and closes the disk images), waits again, and finally SIGKILLs QEMU as a last resort — so halt still completes even when the guest was already halted from inside (e.g. `sudo systemctl halt`), where `system_powerdown` is a no-op
|
|
109
192
|
* advanced networking (requires `advanced_network = true`)
|
|
@@ -143,7 +226,7 @@ to `nil` value to skip related qemu arguments.
|
|
|
143
226
|
* `net_device`: skip all network related arguments:
|
|
144
227
|
* `-device xxx,netdev=net0`
|
|
145
228
|
* `-netdev user,id=net0,xxx`
|
|
146
|
-
* NOTES:
|
|
229
|
+
* NOTES: the provider adds no network; SSH works only if you set up your own network and point `ssh_host` and `ssh_port` at it
|
|
147
230
|
* `drive_interface`: skip drive for the main image, `-drive if=xxx,xxx`
|
|
148
231
|
* `firmware_format`: skip firmware setup for aarch64, `-drive if=pflash,xxx`
|
|
149
232
|
|
|
@@ -436,6 +519,20 @@ Platform support:
|
|
|
436
519
|
|
|
437
520
|
Serial port is exported to unix socket: `<user_home>/.vagrant.d/tmp/vagrant-qemu/<id>/qemu_socket_serial`, or `debug_port`.
|
|
438
521
|
|
|
522
|
+
Run `vagrant serial-config` to list serial endpoints for all machines, or
|
|
523
|
+
`vagrant serial-config NAME` for one machine. The command does not start machines;
|
|
524
|
+
uncreated machines are listed as `NotCreated`. Created machines show `UnixSocket`
|
|
525
|
+
or `Host`/`Port`. Stopped machines report their last-start endpoint when it was recorded, even if
|
|
526
|
+
the configuration has since changed; that endpoint may not be listening.
|
|
527
|
+
Machines using another provider are skipped.
|
|
528
|
+
|
|
529
|
+
Use `vagrant serial-config --json` for an array of machine names and endpoint
|
|
530
|
+
objects. Each record has `machine` (the Vagrant machine name) and `endpoint`.
|
|
531
|
+
A Unix endpoint has `type: "unix"` and `path`; a TCP endpoint has `type: "tcp"`,
|
|
532
|
+
`host` and `port`. An uncreated machine has a `null` endpoint. For newly started instances,
|
|
533
|
+
the command uses the endpoint recorded at launch, even if `debug_port` changes
|
|
534
|
+
in the Vagrantfile; older instances fall back to their current configuration.
|
|
535
|
+
|
|
439
536
|
To debug and login to the GuestOS from serial port:
|
|
440
537
|
|
|
441
538
|
* unix socket
|
|
@@ -510,31 +607,68 @@ vagrant plugin list | grep vagrant-qemu
|
|
|
510
607
|
|
|
511
608
|
> This command will list all installed plugins, and you should see the vagrant-qemu plugin with the locally built version.
|
|
512
609
|
|
|
513
|
-
###
|
|
610
|
+
### Testing
|
|
611
|
+
|
|
612
|
+
Tests are RSpec, in three layers. GitHub Actions runs all of them on every pull request (`.github/workflows/ci.yml`); a new push to a pull request cancels its older run, and changes that only touch `*.md` files are not tested.
|
|
613
|
+
|
|
614
|
+
| Layer | What | Where CI runs it |
|
|
615
|
+
|---|---|---|
|
|
616
|
+
| Unit (`spec/unit`) | Plugin code, no QEMU | Ubuntu, macOS, Windows |
|
|
617
|
+
| Acceptance (`spec/acceptance`) | Vagrant actions against a mock `qemu` (bash scripts) | Ubuntu, macOS |
|
|
618
|
+
| Guest smoke (`examples/ci-smoke`) | Boots a Debian guest: up, ssh, status, halt, destroy | Ubuntu, macOS, Windows WSL2 |
|
|
619
|
+
| e2e (`spec/e2e`) | Real VMs: disks, forwarded ports, provision, reload, package, networking | Ubuntu with KVM; vmnet and socket_vmnet specs on macOS |
|
|
620
|
+
| VirtioFS e2e (`spec/e2e/virtiofs_spec.rb`) | Two resolved shares, host/guest writes, reload, halt/up, daemon and socket cleanup on destroy | Ubuntu, Apple Silicon macOS, Windows WSL2 |
|
|
621
|
+
|
|
622
|
+
CI uses the latest Vagrant release and prints the Ruby and Vagrant versions in each job. Hosted macOS runners have no HVF, so CI runs macOS guests with TCG; the HVF path is only tested locally.
|
|
623
|
+
|
|
624
|
+
Run them locally:
|
|
514
625
|
|
|
515
626
|
```sh
|
|
516
|
-
# Unit
|
|
627
|
+
# Unit and acceptance (fast, no real VM)
|
|
517
628
|
bundle exec rake spec:unit
|
|
518
|
-
|
|
519
|
-
# Acceptance tests (mock QEMU, no real VM)
|
|
520
629
|
bundle exec rake spec:acceptance
|
|
521
630
|
|
|
522
|
-
#
|
|
523
|
-
#
|
|
524
|
-
# a version mismatch):
|
|
631
|
+
# e2e exercises the INSTALLED plugin: rebuild and reinstall first (the
|
|
632
|
+
# suite fails fast on a version mismatch)
|
|
525
633
|
bundle exec rake build
|
|
526
634
|
vagrant plugin install ./pkg/vagrant-qemu-<version>.gem
|
|
527
635
|
TEST_QEMU=1 bundle exec rake spec:e2e
|
|
528
636
|
|
|
529
|
-
#
|
|
530
|
-
|
|
637
|
+
# vmnet (macOS, needs sudo) and socket_vmnet (macOS, needs the daemon:
|
|
638
|
+
# brew install socket_vmnet && sudo brew services start socket_vmnet)
|
|
639
|
+
TEST_VMNET=1 sudo -E bundle exec rspec spec/e2e/advanced_network_spec.rb
|
|
640
|
+
TEST_SOCKET_VMNET=1 bundle exec rspec spec/e2e/socket_vmnet_spec.rb
|
|
531
641
|
|
|
532
|
-
#
|
|
533
|
-
|
|
642
|
+
# VirtioFS: install the host daemon (and compatible QEMU on macOS) first.
|
|
643
|
+
# Uses Linux provider defaults (KVM), macOS TCG, and a box matching the host.
|
|
644
|
+
TEST_VIRTIOFS=1 TEST_BOX=cloud-image/debian-12 bundle exec rspec spec/e2e/virtiofs_spec.rb
|
|
534
645
|
```
|
|
535
646
|
|
|
647
|
+
The e2e boxes default to Apple Silicon (aarch64) boxes; on another host set `TEST_BOX` and `TEST_BOX_CLOUDINIT` to boxes of the host architecture (CI uses `cloud-image/ubuntu-22.04` for both).
|
|
648
|
+
|
|
649
|
+
Adding tests:
|
|
650
|
+
|
|
651
|
+
* A new e2e example in `spec/e2e` tagged `:requires_qemu` runs in CI's Ubuntu e2e job without workflow changes.
|
|
652
|
+
* A feature that needs extra host setup (a daemon, a tool such as `virtiofsd`, root) gets its own tag, an environment switch in `spec/spec_helper.rb` next to `TEST_VMNET`, and a workflow step that installs the dependency and sets the switch.
|
|
653
|
+
|
|
536
654
|
## Known issue / Troubleshooting
|
|
537
655
|
|
|
656
|
+
If boot times out or Vagrant reports an invalid boot state, the provider stops
|
|
657
|
+
the QEMU instance it just launched and keeps its disks. It requests QEMU quit
|
|
658
|
+
directly and allows 3–10 seconds to flush disk writes, based on `qe.graceful_timeout`,
|
|
659
|
+
before falling back to forced termination. Increase `config.vm.boot_timeout` if
|
|
660
|
+
the guest needs more time to become reachable. When QEMU runs on TCG (a guest
|
|
661
|
+
architecture different from the host, or no HVF/KVM available) or several VMs
|
|
662
|
+
boot in parallel, set it to 600 seconds or more, as this project's CI does for
|
|
663
|
+
its TCG runs:
|
|
664
|
+
|
|
665
|
+
```ruby
|
|
666
|
+
config.vm.boot_timeout = 600
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Interrupting the boot wait with Ctrl-C retains the existing behavior and can
|
|
670
|
+
leave QEMU running.
|
|
671
|
+
|
|
538
672
|
### 1. failed to create shared folder
|
|
539
673
|
|
|
540
674
|
```
|
|
@@ -619,9 +753,60 @@ official build from [vagrantup.com](https://www.vagrantup.com/downloads) if
|
|
|
619
753
|
needed). Do **not** work around it by pinning `logger` in a Gemfile — that tends
|
|
620
754
|
to deepen the conflict.
|
|
621
755
|
|
|
756
|
+
### 6. AlmaLinux 9 x86_64 guest times out waiting for SSH on Apple Silicon
|
|
757
|
+
|
|
758
|
+
Check the guest CPU model if the Vagrantfile explicitly sets `qe.cpu = "qemu64"`.
|
|
759
|
+
With the `almalinux/9` box version `9.2.20230513`, that model caused a boot timeout
|
|
760
|
+
in an Apple Silicon test; changing it to `max` allowed the guest to boot and SSH
|
|
761
|
+
successfully. For cross-architecture emulation, use:
|
|
762
|
+
|
|
763
|
+
```ruby
|
|
764
|
+
config.vm.provider "qemu" do |qe|
|
|
765
|
+
qe.arch = "x86_64"
|
|
766
|
+
qe.cpu = "max"
|
|
767
|
+
end
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
The provider already selects `max` by default when the guest architecture differs
|
|
771
|
+
from the host, so removing an explicit `qemu64` override also restores that default.
|
|
772
|
+
CPU models determine which instruction-set features the guest can use; QEMU's
|
|
773
|
+
[CPU ABI table](https://www.qemu.org/docs/master/system/qemu-cpu-models.html#abi-compatibility-levels-for-cpu-models)
|
|
774
|
+
places `qemu64` below the x86-64-v2 baseline required by
|
|
775
|
+
[RHEL 9](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/considerations_in_adopting_rhel_9/ref_architectures_considerations-in-adopting-rhel-9).
|
|
776
|
+
|
|
777
|
+
This workaround is for an x86_64 guest running under TCG emulation. A shell
|
|
778
|
+
provisioner that installs OpenSSH cannot fix a failure before the first SSH
|
|
779
|
+
connection, because provisioning starts after the communicator becomes ready.
|
|
780
|
+
|
|
781
|
+
### Upgrading older primary disk declarations
|
|
782
|
+
|
|
783
|
+
Before the fix in [PR #88](https://github.com/ppggff/vagrant-qemu/pull/88),
|
|
784
|
+
`primary: true` created a separate blank disk, usually `vagrant_primary.qcow2`,
|
|
785
|
+
instead of growing the boot disk. After upgrading, that extra disk is no longer
|
|
786
|
+
automatically attached. Its file is retained with the VM.
|
|
787
|
+
|
|
788
|
+
If you stored data on that extra disk, halt the VM and back up the existing file
|
|
789
|
+
under `.vagrant/machines/<name>/qemu/<id>/vagrant_primary.qcow2`. Replace the old
|
|
790
|
+
primary declaration with a non-primary declaration using the same name:
|
|
791
|
+
|
|
792
|
+
```ruby
|
|
793
|
+
config.vm.disk :disk, name: "vagrant_primary", primary: false, size: "100GB"
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Use the size from your original declaration. The provider reuses the existing
|
|
797
|
+
file without recreating it; if it is missing, a new blank disk would be created.
|
|
798
|
+
If you gave the old disk a custom name, use that name instead. Run `vagrant up
|
|
799
|
+
--provider=qemu` and mount its existing filesystem in the guest as before.
|
|
800
|
+
|
|
801
|
+
If you also want to grow the actual boot disk, add a separate declaration with
|
|
802
|
+
a distinct name, for example:
|
|
803
|
+
|
|
804
|
+
```ruby
|
|
805
|
+
config.vm.disk :disk, name: "boot", primary: true, size: "100GB"
|
|
806
|
+
```
|
|
807
|
+
|
|
622
808
|
## TODO
|
|
623
809
|
|
|
624
810
|
* Support NFS shared folder
|
|
625
|
-
* Support package VM to box
|
|
626
811
|
* More configures
|
|
627
812
|
* GUI mode
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative "../synced_folder_virtiofs"
|
|
2
|
+
|
|
3
|
+
module VagrantPlugins
|
|
4
|
+
module QEMU
|
|
5
|
+
module Action
|
|
6
|
+
class CleanupVirtioFS
|
|
7
|
+
def initialize(app, env)
|
|
8
|
+
@app = app
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
SyncedFolderVirtioFS.new.cleanup(env[:machine], {})
|
|
13
|
+
@app.call(env)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "log4r"
|
|
2
|
+
require "json"
|
|
3
|
+
require "shellwords"
|
|
4
|
+
|
|
5
|
+
module VagrantPlugins
|
|
6
|
+
module QEMU
|
|
7
|
+
module Action
|
|
8
|
+
class MountVirtioFS
|
|
9
|
+
def initialize(app, env)
|
|
10
|
+
@app = app
|
|
11
|
+
@logger = Log4r::Logger.new("vagrant_qemu::action::mount_virtiofs")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(env)
|
|
15
|
+
machine = env[:machine]
|
|
16
|
+
|
|
17
|
+
# prepare sees Vagrant's resolved folder types. Use precisely that
|
|
18
|
+
# mapping, including shares selected through allowed_synced_folder_types.
|
|
19
|
+
mounts_file = machine.data_dir.join("virtiofs", "mounts.json")
|
|
20
|
+
if !machine.provider_config.virtiofs_qemu_args.empty? && mounts_file.file?
|
|
21
|
+
JSON.parse(File.read(mounts_file)).each do |tag, guestpath|
|
|
22
|
+
|
|
23
|
+
escaped_guestpath = Shellwords.escape(guestpath)
|
|
24
|
+
machine.communicate.sudo("mkdir -p #{escaped_guestpath}")
|
|
25
|
+
machine.communicate.sudo("mount -t virtiofs #{Shellwords.escape(tag)} #{escaped_guestpath}")
|
|
26
|
+
machine.ui.info("Mounted virtiofs #{tag} at #{guestpath}")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@app.call(env)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -26,20 +26,26 @@ module VagrantPlugins
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# Update the SSH rule through network(), never in place: a rule from the
|
|
30
|
+
# top-level or box Vagrantfile is one hash shared by every machine.
|
|
31
|
+
has_ssh_forward = machine.config.vm.networks.any? do |type, options|
|
|
32
|
+
type == :forwarded_port && options[:id] == "ssh"
|
|
33
|
+
end
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
# There is no provider-managed SSH forward when the user disables our
|
|
36
|
+
# NIC (e.g. a custom bridged NIC with direct SSH). Vagrant/box configs
|
|
37
|
+
# may still contain an inherited SSH rule: exclude it from collisions.
|
|
38
|
+
if machine.provider_config.net_device.nil?
|
|
39
|
+
machine.config.vm.network(:forwarded_port, id: "ssh", disabled: true) if has_ssh_forward
|
|
40
|
+
@app.call(env)
|
|
41
|
+
return
|
|
40
42
|
end
|
|
41
43
|
|
|
42
|
-
if
|
|
44
|
+
if has_ssh_forward
|
|
45
|
+
machine.config.vm.network(:forwarded_port, id: "ssh",
|
|
46
|
+
host: machine.provider_config.ssh_port,
|
|
47
|
+
auto_correct: machine.provider_config.ssh_auto_correct)
|
|
48
|
+
else
|
|
43
49
|
machine.config.vm.network :forwarded_port,
|
|
44
50
|
:guest => 22,
|
|
45
51
|
:host => machine.provider_config.ssh_port,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative "../synced_folder_virtiofs"
|
|
2
|
+
|
|
3
|
+
module VagrantPlugins
|
|
4
|
+
module QEMU
|
|
5
|
+
module Action
|
|
6
|
+
class ResetVirtioFSArgs
|
|
7
|
+
def initialize(app, env)
|
|
8
|
+
@app = app
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
machine = env[:machine]
|
|
13
|
+
machine.provider_config.virtiofs_qemu_args = []
|
|
14
|
+
SyncedFolderVirtioFS.new.cleanup(machine, {})
|
|
15
|
+
@starting = true
|
|
16
|
+
result = @app.call(env)
|
|
17
|
+
@starting = false
|
|
18
|
+
result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def recover(env)
|
|
22
|
+
machine = env[:machine]
|
|
23
|
+
return unless @starting && !machine.provider_config.virtiofs_qemu_args.empty?
|
|
24
|
+
# Warden unwinds StartInstance first. Once its recovery stops QEMU,
|
|
25
|
+
# the daemons can stop too. A running guest keeps its shares.
|
|
26
|
+
return if machine.provider.driver.running?
|
|
27
|
+
unless env[:virtiofs_logs_shown]
|
|
28
|
+
Dir.glob(machine.data_dir.join("virtiofs/*.log")).each do |path|
|
|
29
|
+
machine.ui.error("virtiofsd log (#{path}):\n#{File.read(path)}")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
SyncedFolderVirtioFS.new.cleanup(machine, preserve_logs: true)
|
|
33
|
+
machine.provider_config.virtiofs_qemu_args = []
|
|
34
|
+
rescue StandardError => error
|
|
35
|
+
machine.ui.error("VirtioFS recovery failed: #{error.message}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "log4r"
|
|
2
|
+
require_relative "../synced_folder_virtiofs"
|
|
2
3
|
|
|
3
4
|
module VagrantPlugins
|
|
4
5
|
module QEMU
|
|
@@ -8,9 +9,11 @@ module VagrantPlugins
|
|
|
8
9
|
def initialize(app, env)
|
|
9
10
|
@app = app
|
|
10
11
|
@logger = Log4r::Logger.new("vagrant_qemu::action::start_instance")
|
|
12
|
+
@start_completed = false
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def call(env)
|
|
16
|
+
@start_completed = false
|
|
14
17
|
fw_ports = forwarded_ports(env)
|
|
15
18
|
config = env[:machine].provider_config
|
|
16
19
|
|
|
@@ -21,11 +24,11 @@ module VagrantPlugins
|
|
|
21
24
|
:machine => config.machine,
|
|
22
25
|
:cpu => config.cpu,
|
|
23
26
|
:smp => config.smp,
|
|
24
|
-
:memory => config.memory,
|
|
27
|
+
:memory => config.virtiofs_qemu_args.empty? ? config.memory : SyncedFolderVirtioFS.shared_memory_size(config.memory),
|
|
25
28
|
:net_device => config.net_device,
|
|
26
29
|
:drive_interface => config.drive_interface,
|
|
27
30
|
:qemu_bin => config.qemu_bin,
|
|
28
|
-
:extra_qemu_args => config.extra_qemu_args,
|
|
31
|
+
:extra_qemu_args => config.extra_qemu_args + config.virtiofs_qemu_args,
|
|
29
32
|
:extra_netdev_args => config.extra_netdev_args,
|
|
30
33
|
:extra_drive_args => config.extra_drive_args,
|
|
31
34
|
:ports => fw_ports,
|
|
@@ -48,7 +51,7 @@ module VagrantPlugins
|
|
|
48
51
|
|
|
49
52
|
# Pick up SSH port that may have been corrected by HandleForwardedPortCollisions
|
|
50
53
|
env[:machine].config.vm.networks.each do |type, opts|
|
|
51
|
-
if type == :forwarded_port && opts[:id] == "ssh"
|
|
54
|
+
if !config.net_device.nil? && type == :forwarded_port && opts[:id] == "ssh" && !opts[:disabled]
|
|
52
55
|
options[:ssh_port] = opts[:host]
|
|
53
56
|
break
|
|
54
57
|
end
|
|
@@ -59,8 +62,69 @@ module VagrantPlugins
|
|
|
59
62
|
options[:private_networks] = private_networks.map { |_, opts| opts }
|
|
60
63
|
|
|
61
64
|
env[:ui].output(I18n.t("vagrant_qemu.starting"))
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
begin
|
|
66
|
+
env[:machine].provider.driver.start(options)
|
|
67
|
+
rescue StandardError
|
|
68
|
+
raise if config.virtiofs_qemu_args.empty?
|
|
69
|
+
log_dir = env[:machine].data_dir.join("virtiofs")
|
|
70
|
+
Dir.glob(log_dir.join("*.log").to_s).each do |path|
|
|
71
|
+
begin
|
|
72
|
+
log = File.read(path)
|
|
73
|
+
env[:ui].error("virtiofsd log (#{path}):\n#{log.empty? ? '(empty)' : log}")
|
|
74
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
75
|
+
# Keep the QEMU failure as the error if the log disappears.
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
# ResetVirtioFSArgs#recover would print the same logs again.
|
|
79
|
+
env[:virtiofs_logs_shown] = true
|
|
80
|
+
Dir.glob(log_dir.join("*.pid").to_s).each do |path|
|
|
81
|
+
begin
|
|
82
|
+
contents = File.read(path).strip
|
|
83
|
+
next unless contents.match?(/\A[1-9]\d*\z/)
|
|
84
|
+
pid = contents.to_i
|
|
85
|
+
alive = begin
|
|
86
|
+
Process.kill(0, pid)
|
|
87
|
+
true
|
|
88
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
socket_path = path.sub(/\.pid$/, ".sock_path")
|
|
92
|
+
socket = File.file?(socket_path) ? File.read(socket_path).strip : nil
|
|
93
|
+
env[:ui].error("virtiofsd pid #{pid} alive=#{alive}, socket=#{socket} socket_exists=#{socket && File.socket?(socket)}")
|
|
94
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
95
|
+
# Keep the QEMU failure as the error if daemon state disappears.
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
raise
|
|
99
|
+
end
|
|
100
|
+
@control_port = options[:control_port]
|
|
101
|
+
@start_completed = true
|
|
102
|
+
result = @app.call(env)
|
|
103
|
+
@start_completed = false
|
|
104
|
+
result
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Warden calls recover with the original failure. Stop only instances
|
|
108
|
+
# successfully launched by this action whose communicator never booted.
|
|
109
|
+
def recover(env)
|
|
110
|
+
return unless @start_completed
|
|
111
|
+
error = env["vagrant.error"]
|
|
112
|
+
return unless error.is_a?(Vagrant::Errors::VMBootTimeout) ||
|
|
113
|
+
error.is_a?(Vagrant::Errors::VMBootBadState)
|
|
114
|
+
|
|
115
|
+
@start_completed = false
|
|
116
|
+
@logger.warn("Boot failed; stopping the QEMU instance")
|
|
117
|
+
# The guest may not be able to handle ACPI yet. Ask QEMU to flush
|
|
118
|
+
# and quit directly, giving it several seconds before SIGKILL.
|
|
119
|
+
timeout = [[env[:machine].provider_config.graceful_timeout, 3].max, 10].min
|
|
120
|
+
driver = env[:machine].provider.driver
|
|
121
|
+
driver.stop(control_port: @control_port, graceful_timeout: timeout, quit_only: true)
|
|
122
|
+
message = driver.running? ? "boot_failed_stop_failed" : "boot_failed_stopped"
|
|
123
|
+
env[:ui].warn(I18n.t("vagrant_qemu.#{message}"))
|
|
124
|
+
rescue StandardError => cleanup_error
|
|
125
|
+
# Preserve the boot error that Warden is already propagating.
|
|
126
|
+
@logger.warn("Could not stop QEMU after boot failure: #{cleanup_error}")
|
|
127
|
+
env[:ui].warn(I18n.t("vagrant_qemu.boot_failed_stop_failed"))
|
|
64
128
|
end
|
|
65
129
|
|
|
66
130
|
def forwarded_ports(env)
|