vagrant-qemu 0.6.2 → 0.6.4
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 +28 -0
- data/CLAUDE.md +37 -1
- data/README.md +4 -2
- data/lib/vagrant-qemu/action/prepare_forwarded_port_collision_params.rb +23 -0
- data/lib/vagrant-qemu/network/vmnet.rb +3 -1
- data/lib/vagrant-qemu/network.rb +8 -3
- data/lib/vagrant-qemu/version.rb +1 -1
- data/spec/e2e/advanced_network_spec.rb +8 -2
- data/spec/e2e/helper.rb +13 -1
- data/spec/e2e/socket_network_spec.rb +39 -3
- data/spec/e2e/socket_vmnet_spec.rb +4 -0
- data/spec/unit/action/prepare_forwarded_port_collision_params_spec.rb +32 -0
- data/spec/unit/driver/start_cmd_advanced_spec.rb +11 -0
- data/spec/unit/network/build_network_config_spec.rb +9 -0
- data/spec/unit/network/vmnet_spec.rb +6 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 275a26aad79e641534b4c9ea0bb7a9824f964b61c66dbbe9e3bf0d7075f23081
|
|
4
|
+
data.tar.gz: f4afe41ed386785952534fff85e7030030fab8b4defdd7438ce37cee649b1a50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b05b9827dc708cf7dab8a417caae11eeed6fda22380e6d80d8eb3fd8a08c974b70f26b32ee0a10ceb06699a69ed59f1dcbfb42b856bed00ddb5bdeefa04acd37
|
|
7
|
+
data.tar.gz: dd55d31e14d55305e1c7714d74eb5d273fed56ba3b4ea927fb37ff305d337dbfd752f913f3b509562c395d8734950079d155e59950138c601041d29b0f10f744
|
data/CHANGELOG.md
CHANGED
|
@@ -195,3 +195,31 @@
|
|
|
195
195
|
that device, just with its output discarded; `-parallel none` — now the
|
|
196
196
|
default — skips creating it entirely, matching how the other default args
|
|
197
197
|
(`-monitor`/`-display`/`-vga`) already disable their optional devices. (#80)
|
|
198
|
+
|
|
199
|
+
# 0.6.3 (2026-08-12)
|
|
200
|
+
|
|
201
|
+
* Fix `vagrant up` aborting with `no implicit conversion of nil into Array`
|
|
202
|
+
on macOS whenever `advanced_network` was enabled without also setting
|
|
203
|
+
`net_mode`. The default `net_mode` of `:auto` selects the vmnet backend but
|
|
204
|
+
was never resolved to a concrete mode, so the backend produced no `-netdev`
|
|
205
|
+
arguments for the private network NIC and the command line assembly blew up
|
|
206
|
+
before QEMU was launched. Setting `net_mode` explicitly (e.g.
|
|
207
|
+
`:vmnet_shared`) was the workaround. (#40)
|
|
208
|
+
|
|
209
|
+
# 0.6.4 (2026-10-09)
|
|
210
|
+
|
|
211
|
+
* Fix `vagrant up` failing with `ForwardPortCollision` ("The forwarded port
|
|
212
|
+
to 50022 is already in use") on macOS 27 even when nothing listens on the
|
|
213
|
+
port. A non-blocking connect to a closed local port there reports success
|
|
214
|
+
on the retry, so Vagrant's port check treated every port as in use. The
|
|
215
|
+
provider now supplies a port check that confirms the connection; results on
|
|
216
|
+
other systems are unchanged, and Windows keeps Vagrant's own check.
|
|
217
|
+
* Quote MAC addresses in the generated cloud-init `network-config`. cloud-init
|
|
218
|
+
reads it as YAML 1.1, where an unquoted MAC whose octets are all decimal and
|
|
219
|
+
below 60 (e.g. `52:54:00:26:47:05`) is a base-60 integer. With
|
|
220
|
+
`advanced_network`, about 1.3% of generated MACs hit this, and the guest's
|
|
221
|
+
network config was rejected, which could leave the VM unreachable over SSH
|
|
222
|
+
on first boot. (#82, thanks @SamuelBoerlin)
|
|
223
|
+
* README: bring a `socket_opts` `listen=`/`connect=` pair up with
|
|
224
|
+
`vagrant up --no-parallel`. A plain `vagrant up` boots both machines in
|
|
225
|
+
parallel, and if the connector starts first the link never comes up.
|
data/CLAUDE.md
CHANGED
|
@@ -23,7 +23,43 @@ vagrant plugin install ./pkg/vagrant-qemu-<version>.gem
|
|
|
23
23
|
vagrant plugin list | grep vagrant-qemu
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Testing
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bundle exec rake spec:unit # unit specs
|
|
30
|
+
bundle exec rake spec:acceptance # acceptance specs
|
|
31
|
+
bundle exec rake spec # unit + acceptance
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Heavy suites are tag-gated in `spec/spec_helper.rb` and excluded unless the matching env var is set: `TEST_QEMU` (`:requires_qemu`), `TEST_VMNET` (`:requires_vmnet`), `TEST_SOCKET_VMNET` (`:requires_socket_vmnet`). rspec prints the excluded tags in its `Run options:` line — check it to confirm the suite you wanted actually ran. The vmnet e2e suite needs root:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
TEST_VMNET=1 sudo -E bundle exec rspec spec/e2e/advanced_network_spec.rb
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**e2e exercises the *installed* plugin, not the source tree.** `spec/e2e/helper.rb` compares the installed plugin version against `lib/vagrant-qemu/version.rb` and aborts on a mismatch, so rebuild and reinstall first:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bundle exec rake build && vagrant plugin install ./pkg/vagrant-qemu-<version>.gem
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That guard compares version *numbers* only. Reinstalling after a source change without bumping the version passes the check while still testing stale code — and produces a local gem whose version collides with a published one.
|
|
47
|
+
|
|
48
|
+
## Releasing
|
|
49
|
+
|
|
50
|
+
`bundle exec rake release` handles the mechanics: tags `v<version>`, pushes the tag, builds, and pushes the gem to rubygems.org. Bump `lib/vagrant-qemu/version.rb` and add the CHANGELOG entry in a separate `Release <version>` commit first (see git history for the shape).
|
|
51
|
+
|
|
52
|
+
A release is complete only when all four have landed:
|
|
53
|
+
|
|
54
|
+
1. gem published on rubygems.org
|
|
55
|
+
2. `master` pushed
|
|
56
|
+
3. tag `v<version>` pushed
|
|
57
|
+
4. GitHub release created **with the built gem attached as an asset** — a repo convention every prior release follows:
|
|
58
|
+
```bash
|
|
59
|
+
gh release create v<version> --title v<version> --notes-file <notes> pkg/vagrant-qemu-<version>.gem
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`rake release` tags before pushing the gem, so a mid-way failure can leave a pushed tag with no published gem; verify all four rather than assuming the command got through.
|
|
27
63
|
|
|
28
64
|
## Architecture
|
|
29
65
|
|
data/README.md
CHANGED
|
@@ -329,7 +329,7 @@ For VM-to-VM networking on macOS without sudo, use `:socket` with a `listen`/`co
|
|
|
329
329
|
```ruby
|
|
330
330
|
Vagrant.configure("2") do |config|
|
|
331
331
|
PORT = 12399
|
|
332
|
-
# vm1 listens
|
|
332
|
+
# vm1 listens, vm2 connects to it.
|
|
333
333
|
config.vm.define "vm1" do |c|
|
|
334
334
|
c.vm.box = "perk/ubuntu-2204-arm64" # an aarch64 cloud-init box
|
|
335
335
|
c.vm.network "private_network", ip: "192.168.105.51"
|
|
@@ -354,6 +354,8 @@ Vagrant.configure("2") do |config|
|
|
|
354
354
|
end
|
|
355
355
|
```
|
|
356
356
|
|
|
357
|
+
Bring it up with `vagrant up --no-parallel`. The provider boots machines in parallel by default, so a plain `vagrant up` can start vm2 before vm1 is listening, and the link never comes up.
|
|
358
|
+
|
|
357
359
|
A single VM with a static IP (vmnet is the default backend on macOS when `net_mode` is `:auto`):
|
|
358
360
|
|
|
359
361
|
```ruby
|
|
@@ -415,7 +417,7 @@ Notes:
|
|
|
415
417
|
* The Linux `:tap` backend expects a pre-created tap device attached to a bridge, e.g.:
|
|
416
418
|
`sudo ip tuntap add tap0 mode tap && sudo ip link set tap0 master br0 && sudo ip link set tap0 up`
|
|
417
419
|
* `socket_opts = "mcast=..."` gives N-way VM-to-VM on Linux/Windows, but does **not** work on macOS: QEMU binds the netdev socket to the multicast group address, which the Darwin socket stack refuses to send from (`EADDRNOTAVAIL`). On macOS use a `listen`/`connect` pair (no root) or vmnet (sudo).
|
|
418
|
-
* `socket_opts = "listen=..."` / `"connect=..."` is a point-to-point QEMU TCP link and connects **exactly two** VMs (QEMU's listening socket accepts a single connection — it is not a hub). You choose which VM listens and which connects. The listener must be running before the connector starts, so define the listener first and bring the environment up
|
|
420
|
+
* `socket_opts = "listen=..."` / `"connect=..."` is a point-to-point QEMU TCP link and connects **exactly two** VMs (QEMU's listening socket accepts a single connection — it is not a hub). You choose which VM listens and which connects. The listener must be running before the connector starts, so define the listener first and bring the environment up with `vagrant up --no-parallel` (a plain `vagrant up` boots both in parallel and may start the connector first: both VMs come up with their IPs, but no traffic passes). Starting a connector alone, or reloading the listener, drops the link.
|
|
419
421
|
|
|
420
422
|
Platform support:
|
|
421
423
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
1
3
|
module VagrantPlugins
|
|
2
4
|
module QEMU
|
|
3
5
|
module Action
|
|
@@ -17,6 +19,13 @@ module VagrantPlugins
|
|
|
17
19
|
remap = {}
|
|
18
20
|
env[:port_collision_remap] = remap
|
|
19
21
|
|
|
22
|
+
# Windows keeps Vagrant's per-interface check for 0.0.0.0.
|
|
23
|
+
if !Vagrant::Util::Platform.windows?
|
|
24
|
+
env[:port_collision_port_check] = lambda do |host_ip, host_port|
|
|
25
|
+
self.class.port_in_use?(host_ip || "0.0.0.0", host_port)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
20
29
|
has_ssh_forward = false
|
|
21
30
|
machine.config.vm.networks.each do |type, options|
|
|
22
31
|
next if type != :forwarded_port
|
|
@@ -42,6 +51,20 @@ module VagrantPlugins
|
|
|
42
51
|
|
|
43
52
|
@app.call(env)
|
|
44
53
|
end
|
|
54
|
+
|
|
55
|
+
# Vagrant's IsPortOpen with an extra getpeername: on macOS 27 a refused
|
|
56
|
+
# non-blocking connect reports EISCONN on the retry, so Socket.tcp
|
|
57
|
+
# returns an unconnected socket and every port looks in use.
|
|
58
|
+
def self.port_in_use?(host, port)
|
|
59
|
+
Socket.tcp(host, port, connect_timeout: 0.1) do |sock|
|
|
60
|
+
sock.remote_address
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
|
|
64
|
+
Errno::ENETUNREACH, Errno::EACCES, Errno::ENOTCONN, Errno::EALREADY,
|
|
65
|
+
Errno::EINVAL
|
|
66
|
+
false
|
|
67
|
+
end
|
|
45
68
|
end
|
|
46
69
|
end
|
|
47
70
|
end
|
|
@@ -8,8 +8,10 @@ module VagrantPlugins
|
|
|
8
8
|
# Supports vmnet-shared, vmnet-host, vmnet-bridged
|
|
9
9
|
class Vmnet < Base
|
|
10
10
|
def build_netdev_args(id, options)
|
|
11
|
+
# :auto reaches this backend only on macOS, where it means vmnet-shared;
|
|
12
|
+
# net_mode is not rewritten during backend selection, so match it here.
|
|
11
13
|
case options[:net_mode]
|
|
12
|
-
when :vmnet_shared
|
|
14
|
+
when :vmnet_shared, :auto
|
|
13
15
|
base = "vmnet-shared,id=#{id}"
|
|
14
16
|
base += subnet_args(options)
|
|
15
17
|
%W(-netdev #{base})
|
data/lib/vagrant-qemu/network.rb
CHANGED
|
@@ -100,10 +100,15 @@ module VagrantPlugins
|
|
|
100
100
|
# @param mac1 [String] MAC of advanced network NIC
|
|
101
101
|
# @param ip [String] static IP for the advanced NIC (e.g. "192.168.105.10")
|
|
102
102
|
# @param netmask [String] netmask (e.g. "255.255.255.0")
|
|
103
|
-
#
|
|
103
|
+
# Emitted as JSON, which is valid YAML: cloud-init parses network-config
|
|
104
|
+
# as YAML 1.1, where an unquoted MAC whose octets are all decimal and
|
|
105
|
+
# below 60 (e.g. 52:54:00:26:47:05) is a base-60 integer. Psych's
|
|
106
|
+
# to_yaml leaves such a MAC unquoted; JSON always quotes strings.
|
|
107
|
+
#
|
|
108
|
+
# @return [String] YAML (JSON) string
|
|
104
109
|
def self.build_network_config(mac0:, mac1:, ip:, netmask: "255.255.255.0")
|
|
105
110
|
require 'ipaddr'
|
|
106
|
-
require '
|
|
111
|
+
require 'json'
|
|
107
112
|
|
|
108
113
|
prefix = IPAddr.new(netmask).to_i.to_s(2).count("1")
|
|
109
114
|
|
|
@@ -123,7 +128,7 @@ module VagrantPlugins
|
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
config
|
|
131
|
+
JSON.pretty_generate(config)
|
|
127
132
|
end
|
|
128
133
|
end
|
|
129
134
|
end
|
data/lib/vagrant-qemu/version.rb
CHANGED
|
@@ -10,9 +10,13 @@ describe "advanced networking end-to-end", :requires_vmnet do
|
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
# net_mode is deliberately left at its default (:auto) here — the other
|
|
14
|
+
# examples cover it set explicitly. Issue #40 was reported against exactly
|
|
15
|
+
# this shape, which no test in any layer exercised.
|
|
16
|
+
it "VM gets the configured static IP with the default net_mode" do
|
|
14
17
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
15
18
|
Vagrant.configure("2") do |config|
|
|
19
|
+
#{snapd_fast_stop}
|
|
16
20
|
config.vm.box = "#{test_box_cloudinit}"
|
|
17
21
|
config.vm.box_check_update = false
|
|
18
22
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -20,7 +24,6 @@ describe "advanced networking end-to-end", :requires_vmnet do
|
|
|
20
24
|
config.vm.provider "qemu" do |qe|
|
|
21
25
|
qe.memory = "2G"
|
|
22
26
|
qe.advanced_network = true
|
|
23
|
-
qe.net_mode = :vmnet_shared
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
RUBY
|
|
@@ -33,6 +36,7 @@ describe "advanced networking end-to-end", :requires_vmnet do
|
|
|
33
36
|
it "host can ping the VM IP" do
|
|
34
37
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
35
38
|
Vagrant.configure("2") do |config|
|
|
39
|
+
#{snapd_fast_stop}
|
|
36
40
|
config.vm.box = "#{test_box_cloudinit}"
|
|
37
41
|
config.vm.box_check_update = false
|
|
38
42
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -56,6 +60,7 @@ describe "advanced networking end-to-end", :requires_vmnet do
|
|
|
56
60
|
user_mac = "52:54:00:aa:bb:cc"
|
|
57
61
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
58
62
|
Vagrant.configure("2") do |config|
|
|
63
|
+
#{snapd_fast_stop}
|
|
59
64
|
config.vm.box = "#{test_box_cloudinit}"
|
|
60
65
|
config.vm.box_check_update = false
|
|
61
66
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -78,6 +83,7 @@ describe "advanced networking end-to-end", :requires_vmnet do
|
|
|
78
83
|
it "two VMs can communicate via private network" do
|
|
79
84
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
80
85
|
Vagrant.configure("2") do |config|
|
|
86
|
+
#{snapd_fast_stop}
|
|
81
87
|
config.vm.define "vm1" do |c|
|
|
82
88
|
c.vm.box = "#{test_box_cloudinit}"
|
|
83
89
|
c.vm.box_check_update = false
|
data/spec/e2e/helper.rb
CHANGED
|
@@ -46,6 +46,15 @@ module E2EHelper
|
|
|
46
46
|
TEST_BOX_CLOUDINIT
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# The cloud-init box starts a snapd auto-refresh on first boot, and stopping
|
|
50
|
+
# snapd mid-refresh can hold guest shutdown for up to 90s. Destroy follows
|
|
51
|
+
# right after, so don't wait for it.
|
|
52
|
+
def snapd_fast_stop
|
|
53
|
+
<<~RUBY.chomp
|
|
54
|
+
config.vm.provision "shell", inline: "mkdir -p /etc/systemd/system/snapd.service.d && printf '[Service]\\\\nTimeoutStopSec=5s\\\\n' > /etc/systemd/system/snapd.service.d/e2e-fast-stop.conf && systemctl daemon-reload"
|
|
55
|
+
RUBY
|
|
56
|
+
end
|
|
57
|
+
|
|
49
58
|
def vagrant_up(cwd, provider: "qemu", timeout: 300)
|
|
50
59
|
run_vagrant_cmd("up", "--provider=#{provider}", cwd: cwd, timeout: timeout)
|
|
51
60
|
end
|
|
@@ -65,7 +74,10 @@ module E2EHelper
|
|
|
65
74
|
run_vagrant_cmd("reload", cwd: cwd, timeout: timeout)
|
|
66
75
|
end
|
|
67
76
|
|
|
68
|
-
|
|
77
|
+
# Longer than the driver's own escalation (powerdown, then quit, each up to
|
|
78
|
+
# graceful_timeout) for two machines: killing vagrant mid-stop leaves the
|
|
79
|
+
# daemonized QEMU running and holding its ports for the next example.
|
|
80
|
+
def vagrant_destroy(cwd, timeout: 300)
|
|
69
81
|
run_vagrant_cmd("destroy", "-f", cwd: cwd, timeout: timeout)
|
|
70
82
|
end
|
|
71
83
|
|
|
@@ -36,6 +36,7 @@ describe "advanced networking over socket multicast (no root)", :requires_qemu d
|
|
|
36
36
|
# assignment or cross-VM traffic broke.
|
|
37
37
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
38
38
|
Vagrant.configure("2") do |config|
|
|
39
|
+
#{snapd_fast_stop}
|
|
39
40
|
config.vm.box = "#{test_box_cloudinit}"
|
|
40
41
|
config.vm.box_check_update = false
|
|
41
42
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -57,6 +58,37 @@ describe "advanced networking over socket multicast (no root)", :requires_qemu d
|
|
|
57
58
|
expect(ssh[:stdout]).to include("192.168.105.40")
|
|
58
59
|
end
|
|
59
60
|
|
|
61
|
+
it "a MAC that YAML 1.1 reads as an integer reaches netplan as a string" do
|
|
62
|
+
# All octets decimal and below 60: an unquoted MAC in network-config is a
|
|
63
|
+
# base-60 integer to cloud-init, and netplan then rejects the rendered
|
|
64
|
+
# config. Generated MACs hit this ~1.3% of the time, so pin one.
|
|
65
|
+
mac = "52:54:00:26:47:05"
|
|
66
|
+
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
67
|
+
Vagrant.configure("2") do |config|
|
|
68
|
+
#{snapd_fast_stop}
|
|
69
|
+
config.vm.box = "#{test_box_cloudinit}"
|
|
70
|
+
config.vm.box_check_update = false
|
|
71
|
+
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
72
|
+
config.vm.network "private_network", ip: "192.168.105.42", mac: "#{mac}"
|
|
73
|
+
config.vm.provider "qemu" do |qe|
|
|
74
|
+
qe.memory = "2G"
|
|
75
|
+
qe.advanced_network = true
|
|
76
|
+
qe.net_mode = :socket
|
|
77
|
+
qe.mcast_addr = "#{MCAST}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
RUBY
|
|
81
|
+
|
|
82
|
+
result = vagrant_up(@work_dir, timeout: 600)
|
|
83
|
+
expect(result[:exit_code]).to eq 0
|
|
84
|
+
|
|
85
|
+
ssh = vagrant_ssh(@work_dir, command: "ip addr show")
|
|
86
|
+
expect(ssh[:stdout]).to include("192.168.105.42")
|
|
87
|
+
|
|
88
|
+
ssh = vagrant_ssh(@work_dir, command: "sudo netplan generate")
|
|
89
|
+
expect(ssh[:exit_code]).to eq(0), ssh[:stderr]
|
|
90
|
+
end
|
|
91
|
+
|
|
60
92
|
it "two VMs communicate over the socket multicast private network" do
|
|
61
93
|
# QEMU's socket-multicast netdev binds its UDP socket to the multicast
|
|
62
94
|
# group address (net/socket.c net_socket_mcast_create). Darwin's socket
|
|
@@ -73,6 +105,7 @@ describe "advanced networking over socket multicast (no root)", :requires_qemu d
|
|
|
73
105
|
|
|
74
106
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
75
107
|
Vagrant.configure("2") do |config|
|
|
108
|
+
#{snapd_fast_stop}
|
|
76
109
|
config.vm.define "vm1" do |c|
|
|
77
110
|
c.vm.box = "#{test_box_cloudinit}"
|
|
78
111
|
c.vm.box_check_update = false
|
|
@@ -135,8 +168,8 @@ describe "advanced networking over socket listen/connect (no root)", :requires_q
|
|
|
135
168
|
it "two VMs communicate over a user-defined listen/connect socket" do
|
|
136
169
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
137
170
|
Vagrant.configure("2") do |config|
|
|
138
|
-
#
|
|
139
|
-
#
|
|
171
|
+
#{snapd_fast_stop}
|
|
172
|
+
# The user picks the roles: vm1 listens, vm2 connects.
|
|
140
173
|
config.vm.define "vm1" do |c|
|
|
141
174
|
c.vm.box = "#{test_box_cloudinit}"
|
|
142
175
|
c.vm.box_check_update = false
|
|
@@ -167,7 +200,10 @@ describe "advanced networking over socket listen/connect (no root)", :requires_q
|
|
|
167
200
|
end
|
|
168
201
|
RUBY
|
|
169
202
|
|
|
170
|
-
|
|
203
|
+
# The provider boots machines in parallel; --no-parallel follows the
|
|
204
|
+
# definition order, so vm1 is listening before vm2 connects.
|
|
205
|
+
result = run_vagrant_cmd("up", "--provider=qemu", "--no-parallel", cwd: @work_dir, timeout: 600)
|
|
206
|
+
expect(result[:exit_code]).to eq(0), result[:stderr]
|
|
171
207
|
|
|
172
208
|
# vm1 (listener) pings vm2 (connector) over the link. Assert on the ping
|
|
173
209
|
# output itself, not just an exit code that can pass vacuously.
|
|
@@ -16,6 +16,7 @@ describe "socket_vmnet advanced networking end-to-end", :requires_socket_vmnet d
|
|
|
16
16
|
it "VM gets the configured static IP (no sudo)" do
|
|
17
17
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
18
18
|
Vagrant.configure("2") do |config|
|
|
19
|
+
#{snapd_fast_stop}
|
|
19
20
|
config.vm.box = "#{test_box_cloudinit}"
|
|
20
21
|
config.vm.box_check_update = false
|
|
21
22
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -36,6 +37,7 @@ describe "socket_vmnet advanced networking end-to-end", :requires_socket_vmnet d
|
|
|
36
37
|
it "host can ping the VM IP" do
|
|
37
38
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
38
39
|
Vagrant.configure("2") do |config|
|
|
40
|
+
#{snapd_fast_stop}
|
|
39
41
|
config.vm.box = "#{test_box_cloudinit}"
|
|
40
42
|
config.vm.box_check_update = false
|
|
41
43
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -56,6 +58,7 @@ describe "socket_vmnet advanced networking end-to-end", :requires_socket_vmnet d
|
|
|
56
58
|
it "two VMs can communicate via the socket_vmnet private network" do
|
|
57
59
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
58
60
|
Vagrant.configure("2") do |config|
|
|
61
|
+
#{snapd_fast_stop}
|
|
59
62
|
config.vm.define "vm1" do |c|
|
|
60
63
|
c.vm.box = "#{test_box_cloudinit}"
|
|
61
64
|
c.vm.box_check_update = false
|
|
@@ -100,6 +103,7 @@ describe "socket_vmnet advanced networking end-to-end", :requires_socket_vmnet d
|
|
|
100
103
|
shim = File.expand_path("support/qemu_no_stream_shim.sh", __dir__)
|
|
101
104
|
File.write(@work_dir.join("Vagrantfile"), <<~RUBY)
|
|
102
105
|
Vagrant.configure("2") do |config|
|
|
106
|
+
#{snapd_fast_stop}
|
|
103
107
|
config.vm.box = "#{test_box_cloudinit}"
|
|
104
108
|
config.vm.box_check_update = false
|
|
105
109
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
|
@@ -69,4 +69,36 @@ describe VagrantPlugins::QEMU::Action::PrepareForwardedPortCollisionParams do
|
|
|
69
69
|
|
|
70
70
|
expect(ssh_entry[:host]).to eq 60022
|
|
71
71
|
end
|
|
72
|
+
|
|
73
|
+
describe "port collision check" do
|
|
74
|
+
def port_check(windows: false)
|
|
75
|
+
allow(Vagrant::Util::Platform).to receive(:windows?).and_return(windows)
|
|
76
|
+
ctx = mock_vagrant_env(networks: [])
|
|
77
|
+
described_class.new(app, ctx[:env]).call(ctx[:env])
|
|
78
|
+
ctx[:env][:port_collision_port_check]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "reports a listening port as in use" do
|
|
82
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
83
|
+
expect(port_check.call("127.0.0.1", server.addr[1])).to eq true
|
|
84
|
+
ensure
|
|
85
|
+
server&.close
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "reports a closed port as free" do
|
|
89
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
90
|
+
port = server.addr[1]
|
|
91
|
+
server.close
|
|
92
|
+
expect(port_check.call("127.0.0.1", port)).to eq false
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "checks 0.0.0.0 when no host_ip is given" do
|
|
96
|
+
expect(described_class).to receive(:port_in_use?).with("0.0.0.0", 50022).and_return(false)
|
|
97
|
+
port_check.call(nil, 50022)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "keeps Vagrant's own check on Windows" do
|
|
101
|
+
expect(port_check(windows: true)).to be_nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
72
104
|
end
|
|
@@ -67,6 +67,17 @@ describe VagrantPlugins::QEMU::Driver, "start command line (dual NIC)" do
|
|
|
67
67
|
expect(cmd_str).to include("-netdev vmnet-shared,id=net1")
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# Regression for issue #40: the default net_mode :auto selected the Vmnet
|
|
71
|
+
# backend but matched none of its cases, so NIC 1 args came back nil.
|
|
72
|
+
it "builds NIC 1 args for the default net_mode :auto" do
|
|
73
|
+
allow(VagrantPlugins::QEMU::Network).to receive(:auto_detect)
|
|
74
|
+
.and_return(VagrantPlugins::QEMU::Network::Vmnet.new)
|
|
75
|
+
subject.start(advanced_options.merge(net_mode: :auto))
|
|
76
|
+
cmd_str = @captured_cmd.join(" ")
|
|
77
|
+
expect(cmd_str).to include("-netdev vmnet-shared,id=net1")
|
|
78
|
+
expect(cmd_str).to include("start-address=192.168.105.1")
|
|
79
|
+
end
|
|
80
|
+
|
|
70
81
|
it "NIC 1 has different MAC than NIC 0" do
|
|
71
82
|
subject.start(advanced_options)
|
|
72
83
|
cmd_str = @captured_cmd.join(" ")
|
|
@@ -29,6 +29,15 @@ describe VagrantPlugins::QEMU::Network, ".build_network_config" do
|
|
|
29
29
|
expect(priv_nic["addresses"]).to eq ["192.168.105.10/24"]
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
context "with MACs that YAML 1.1 reads as base-60 integers" do
|
|
33
|
+
let(:mac0) { "52:54:00:26:47:05" }
|
|
34
|
+
let(:mac1) { "52:54:00:12:34:56" }
|
|
35
|
+
|
|
36
|
+
it "quotes the MACs" do
|
|
37
|
+
expect(subject).to include("\"#{mac0}\"", "\"#{mac1}\"")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
32
41
|
it "calculates /16 prefix for 255.255.0.0 netmask" do
|
|
33
42
|
result = described_class.build_network_config(
|
|
34
43
|
mac0: mac0, mac1: mac1,
|
|
@@ -20,6 +20,12 @@ describe VagrantPlugins::QEMU::Network::Vmnet do
|
|
|
20
20
|
expect(netdev_arg).to include("subnet-mask=255.255.255.0")
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
it "treats :auto as vmnet-shared (macOS default)" do
|
|
24
|
+
pn = [{ ip: "192.168.105.10", netmask: "255.255.255.0" }]
|
|
25
|
+
args = subject.build_netdev_args("net1", net_mode: :auto, private_networks: pn)
|
|
26
|
+
expect(args).to eq subject.build_netdev_args("net1", net_mode: :vmnet_shared, private_networks: pn)
|
|
27
|
+
end
|
|
28
|
+
|
|
23
29
|
it "builds vmnet-host args with subnet" do
|
|
24
30
|
args = subject.build_netdev_args("net1",
|
|
25
31
|
net_mode: :vmnet_host,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vagrant-qemu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ppggff
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Enables Vagrant to manage machines with QEMU.
|
|
14
14
|
email: pgf00a@gmail.com
|